Django app
- users-用户管理
- course-课程管理
- organization-机构和教师管理
- operation-用户操作管理
C:\Users\jianhu.yong>mkvirtualenv mxonline |



数据库配置DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mxonline',
'USER':'root',
'PASSWORD':'root',
'HOST':'127.0.0.1'
}
}
新建数据库

生成默认数据表makemigrations
migtate
userapp model设计manage.py@MxOnline > startapp users
"C:\Program Files\JetBrains\PyCharm 2017.1.2\bin\runnerw.exe" C:\Users\jianhu.yong\Envs\mxonline\Scripts\python.exe "C:\Program Files\JetBrains\PyCharm 2017.1.2\helpers\pycharm\django_manage.py" startapp users C:/Users/jianhu.yong/PycharmProjects/MxOnline
Following files were affected
C:\Users\jianhu.yong\PycharmProjects\MxOnline\users\tests.py
C:\Users\jianhu.yong\PycharmProjects\MxOnline\users\__init__.py
C:\Users\jianhu.yong\PycharmProjects\MxOnline\users\apps.py
C:\Users\jianhu.yong\PycharmProjects\MxOnline\users\admin.py
C:\Users\jianhu.yong\PycharmProjects\MxOnline\users\models.py
C:\Users\jianhu.yong\PycharmProjects\MxOnline\users\views.py
C:\Users\jianhu.yong\PycharmProjects\MxOnline\users\migrations\__init__.py
Process finished with exit code 0

默认的user表不满足我们的需求,因此需要定制user表
自定义userprofile覆盖默认user表
class UserProfile(AbstractUser): |
INSTALLED_APPS = [ |

(mxonline) C:\Users\jianhu.yong\Downloads>pip install pillow |

manage.py@MxOnline > makemigrations users |
manage.py@MxOnline > migrate users |
app设计需要避免循环引用(a调用b,b调用a,最后ab都无法执行)

EmailVerifyRecord邮箱验证码设计class EmailVerifyRecord(models.Model):
code = models.CharField(max_length=20, verbose_name=u"验证码")
email = models.EmailField(max_length=50, verbose_name=u"邮箱")
send_type = models.CharField(choices=(("register", u"注册"),("forget", u"找回密码")), max_length=10)
send_time = models.DateTimeField(default=datetime.now)
# datetime.now() model编译时间生成默认时间
# datetime.now classs实例化时生成的时间
class Meta:
verbose_name = u"邮箱验证码"
verbose_name_plural = verbose_name
```
PageBanner轮播图
``` python
class Banner(models.Model):
title = models.CharField(max_length=100, verbose_name=u"标题")
image = models.ImageField(upload_to="banner/%Y/%m/", verbose_name=u"轮播图", max_length=100)
url = models.URLField(max_length=200, verbose_name=u"访问地址")
index = models.IntegerField(default=100,verbose_name=u"顺序")
add_time = models.DateTimeField(default=datetime.now, verbose_name=u"添加时间")
class Meta:
verbose_name = u"轮播图"
verbose_name_plural = verbose_name
```
courses models设计:
新建app
- courses课程基本信息
- lesson 章节信息
- video 视频信息
- courseResouce 课程资源
``` python models.py
# conding:utf8
from __future__ import unicode_literals
from datetime import datetime
from django.db import models
# Create your models here.
class Course(models.Model):
name = models.CharField(max_length=50, verbose_name=u"课程名")
desc = models.CharField(max_length=300, verbose_name=u"课程描述")
detail = models.TextField(verbose_name=u"课程详情")
# TextField不限制max_length
defree = models.CharField(choices=(("cj", u"初级"),("zj", u"中级"),("bj", u"高级")), max_length=2)
learn_times = models.IntegerField(default=0, verbose_name=u"学习时长(分钟数)")
students= models.IntegerField(default=0, verbose_name=u"学习人数")
fav_nums= models.IntegerField(default=0, verbose_name=u"收藏人数")
image = models.ImageField(upload_to="courses/%y/%m", verbose_name=u"封面图", max_length=100)
click_nums = models.ImageField(default=0, verbose_name=u"点击数")
add_time = models.DateTimeField(default=datetime.now, verbose_name=u"添加时间")
class Meta:
verbose_name = u"课程"
verbose_name_plural = verbose_name
class Lesson(models.Model):
course = models.ForeignKey(Course, verbose_name=u"课程")
name = models.CharField(max_length=100 , verbose_name=u"章节名")
add_time = models.DateTimeField(default=datetime.now, verbose_name=u"添加时间")
class Meta:
verbose_name = u"章节"
verbose_name_plural = verbose_name
class Video(models.Model):
lesson = models.ForeignKey(Lesson, verbose_name=u"章节")
name = models.CharField(max_length=100 , verbose_name=u"视频名")
add_time = models.DateTimeField(default=datetime.now, verbose_name=u"添加时间")
class Meta:
verbose_name = u"视频"
verbose_name_plural = verbose_name
class CourseResource(models.Model):
course = models.ForeignKey(Course, verbose_name=u"课程")
name = models.CharField(max_length=100 , verbose_name=u"名称")
download = models.FileField(upload_to="course/resourse/%Y/%m", verbose_name=u"资源文件", max_length=100)
add_time = models.DateTimeField(default=datetime.now, verbose_name=u"添加时间")
class Meta:
verbose_name = u"课程资源"
verbose_name_plural = verbose_name
manage.py@MxOnline > startapp organization |
- CourseOrg 课程机构基本信息
- Teacher 教师基本信息
- CityDict 城市信息
# coding:utf8 |
# coding:utf8 |
添加至install apps settings.py
manage.py@MxOnline > makemigrations |
新建apps包统一管理app



默认打钩,会在导入模块内容之前加入apps.xxx

通过设置解决import出错问题
但是python 运行会提示模块找不到错误
settings.py
后台管理系统
- 权限管理
- 少前端样式
- 快速开发
http://127.0.0.1:8000/admin
创建diango admin后台用户出错

重新修改下即可!
manage.py@MxOnline > createsuperuser |
修改django admin修改为中文
settings.pyLANGUAGE_CODE = 'zh-hans'
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
#需要改成False,否侧django在数据库中存的时间为UTC国际时间!
USE_TZ = False
xadmin设置
(mxonline) C:\Users\jianhu.yong\PycharmProjects\MxOnline>pip install xadmin |
INSTALLED_APPS = [ |
from django.conf.urls import url |

manage.py@MxOnline > makemigrations |
http://sshwsfc.github.io/xadmin/
基于源码安装xadmin
- 可自定义
- 功能最新 比pypi
https://github.com/sshwsfc/xadmin





BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |



pip install future |
manage.py@MxOnline > makemigrations |
设置脚本文件模版neirong

通过xadmin注册model

注册剩余models
xadmin全局配置
from xadmin import views |
class GlobalSettings(object): |

xadmin主题bug[SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure
flag = False |

修改为中文显示
default_app_config = 'operation.apps.OperationConfig' |
# coding:utf8 |
首页和登录页的配置url(r'^$', TemplateView.as_view(template_name='index.html'), name='index'),
url(r'^login/$', TemplateView.as_view(template_name='login.html'), name='login'),
用户登录配置
urlpatterns = [ |
def user_login(request): |
AUTHENTICATION_BACKENDS = ( |
views方法基于类,不基于函数书写
from django.views.generic.base import View |
form实现登入

session & cookies自动登录机制
- cookies 浏览器本地存储方式机制,可以存储很多信息,存储在某个域名之下,存储在本地不安全!
- session 服务器生成,存储在服务器端,安全,发送给用户,存储在cookies之中!




value 用户名密码加密字符串,服务器对session id取出value 并解密,实现自动登录 !

注释掉 自动登录就失效了!
用户注册
{% load staticfiles %} |
验证码插件
https://github.com/mbi/django-simple-captcha
Install django-simple-captcha via pip: pip install django-simple-captcha |
pip install django-simple-captcha==0.4.6 |
配置邮箱设置!# Mail Config
EMAIL_HOST = 'smtp.laohulab.com'
EMAIL_PORT = 25
EMAIL_HOST_USER = 'postmaster@qq.com'
EMAIL_HOST_PASSWORD = 'XXXX'
EMAIL_USE_TLS =False
EMAIL_FROM = 'postmaster@qq.com'
注册邮件发送# coding:utf8
__author__ = 'jianhu.yong'
__date__ = '2018/7/5 16:29'
from random import Random
from django.core.mail import send_mail
from users.models import EmailVerifyRecord
from MxOnline.settings import EMAIL_FROM
def random_str(randomlength=8):
str = ''
chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
length = len(chars) -1
random =Random()
for i in range(randomlength):
str += chars[random.randint(0, length)]
return str
def send_register_email(email, send_type='register'):
email_record = EmailVerifyRecord()
code = random_str(16)
email_record.code = code
email_record.email = email
email_record.send_type = send_type
email_record.save()
email_title = ''
email_body = ''
if send_type == 'register':
email_title = 'MX在线网注册激活链接'
email_body = '请点击下面的链接激活你的账号:http://127.0.0.1:8000/active/{0}'.format(code)
send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
if send_status:
pass
else:
pass
elif send_type == 'forget':
email_title = 'MX在线网密码重置链接'
email_body = '请点击下面的链接重置密码:http://127.0.0.1:8000/reset/{0}'.format(code)
send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
if send_status:
pass
激活用户设置 url(r'^active/(?P<active_code>\w+)/$', ActiveUserView.as_view(), name='user_active'),
class ActiveUserView(View):
def get(self, request, active_code):
all_records = EmailVerifyRecord.objects.filter(code=active_code)
if all_records:
for record in all_records:
email = record.email
user = UserProfile.objects.get(email=email)
user.is_active = True
user.save()
else:
return render(request, 'active_fail.html')
return render(request, 'login.html')
```
密码找回
``` python
url(r'^forget/$', ForgetPwdView.as_view(), name='forget_pwd'),
url(r'^reset/(?P<active_code>\w+)/$', ResetView.as_view(), name='reset_pwd'),
url(r'^modify/$', ModifyPwdView.as_view(), name='modify_pwd'),
class RegisterView(View):
def get(self, request):
register_form = RegisterForm()
return render(request, 'register.html', {'register_form':register_form})
def post(self, request):
register_form = RegisterForm(request.POST)
if register_form.is_valid():
user_name = request.POST.get('email', '')
if UserProfile.objects.filter(email=user_name):
return render(request, 'register.html', {'msg':'用户已存在!','register_form': register_form})
pass_word = request.POST.get('password', '')
user_profile =UserProfile()
user_profile.username = user_name
user_profile.email = user_name
user_profile.is_active = False
user_profile.password = make_password(pass_word)
user_profile.save()
send_register_email(user_name, 'register')
return render(request, 'login.html')
else:
return render(request, 'register.html', {'register_form': register_form})
class ForgetPwdView(View):
def get(self, request):
forgetpwd_form = ForgetPwdForm()
return render(request, 'forgetpwd.html', {'forgetpwd_form':forgetpwd_form})
def post(self, request):
forgetpwd_form = ForgetPwdForm(request.POST)
if forgetpwd_form.is_valid():
email = request.POST.get('email', '')
send_register_email(email, 'forget')
return render(request, 'send_success.html')
else:
return render(request, 'forgetpwd.html', {'forgetpwd_form':forgetpwd_form})
class ResetView(View):
def get(self, request, active_code):
all_records = EmailVerifyRecord.objects.filter(code=active_code)
if all_records:
for record in all_records:
email = record.email
return render(request, 'password_reset.html', {'email':email})
else:
return render(request, 'active_fail.html')
return render(request, 'login.html')
class ModifyPwdView(View):
def post(self, request):
modify_form = ModifyPwdForm(request.POST)
if modify_form.is_valid():
pwd1 = request.POST.get('password1', '')
pwd2 = request.POST.get('password2', '')
email = request.POST.get('email', '')
if pwd1 != pwd2:
return render(request, 'password_reset.html', {'email':email, 'msg':'密码不一致!'})
user = UserProfile.objects.get(email=email)
user.password = make_password(pwd2)
user.save()
return render(request, 'login.html')
else:
email = request.POST.get('email', '')
return render(request, 'password_reset', {'email':email, 'modify_form':modify_form})
MEDIA_URL配置 调用静态imageTEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'django.core.context_processors.media'
],
},
},
]
# upload setting |
# 配置上传文件的访问处理函数
url(r'^media/(?P<path>.*)$', serve, {'document_root':MEDIA_ROOT}),
from django.views.static import serve
from MxOnline.settings import MEDIA_ROOT
django分页功能!
https://github.com/jamespacileo/django-pure-pagination

p = Paginator(all_orgs, request=request)
p = Paginator(all_orgs, 5, request=request)

使用默认,分页样式无法控制
列表筛选功能
机构排名和课程学习人数排序
modelform提交
外键关系{{ course.lesson_set.all.count }}
{% for user_course in course.usercourse_set.all %}
<span class="pic"><img width="40" height="40" src="{{ MEDIA_URL }}{{ user_course.user.image }}"/></span>
{% endfor %}
def get_zj_nums(self):
return self.lesson_set.all().count()
def get_learn_users(self):
return self.usercourse_set.all()[:5]
{{course.get_zj_nums}}
{% for usercourse in course.get_learn_users %}
用户上传文件
html
enctype=”multipart/form-data”
后端:class UploadImageView(LoginRequiredMixin, View):
def post(self, request):
image_form = UploadImageForm(request.POST, request.FILES)
if image_form.is_valid():
image = image_form.cleaned_data['image']
request.user.image = image
request.user.save()
pass
# def post(self, request):
# image_form = UploadImageForm(request.POST, request.FILES, instance=request.user)
# if image_form.is_valid():
# image_form.save()
```
4040 500配置
django xadmin 进阶开发
debug 改成true时候
注释
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
url(r'^static/(?P<path>.*)$', serve, {'document_root':STATIC_ROOT}),
model icon配置
model_icon = 'fa fa-user'
开源图标库
https://fontawesome.com/

倒叙排列
ordering = ['-click_nums']
设置字段只读
readonly_fields = ['click_nums', 'fav_nums']
移除某个字段显示
exclude = ['fav_nums']
需要注意是否和readonly_fields冲突
关联外键字段,添加搜索功能(针对外键数量很多是,自动搜索方便)
relfield_style = 'fk-ajax'
报错
TypeError: Related Field got invalid lookup: icontains
1
原因
出错原因是你配置的ModelAdmin类中,元组search_fields中的项不是字符类型。
解决
删除元组search_fields中类型不符的项
xadmin inline模式
``` python
class LessonInline(object):
model = Lesson
extra = 0
class CourseAdmin(object):
list_display = ['name','desc','detail','degree','learn_times','students','fav_nums','image','click_nums','add_time']
search_fields = ['name','desc','detail','degree','learn_times','students','fav_nums','image','click_nums']
list_filter = ['name','desc','detail','degree','learn_times','students','fav_nums','image','click_nums','add_time']
ordering = ['-click_nums']
readonly_fields = ['click_nums']
exclude = ['fav_nums']
inlines = [LessonInline]
xadmin.site.register(Course, CourseAdmin)
class LessonInline(object):
model = Lesson
extra = 0
class CourseResourceInline(object):
model = CourseResource
extra = 0
class CourseAdmin(object):
list_display = ['name','desc','detail','degree','learn_times','students','fav_nums','image','click_nums','add_time']
search_fields = ['name','desc','detail','degree','learn_times','students','fav_nums','image','click_nums']
list_filter = ['name','desc','detail','degree','learn_times','students','fav_nums','image','click_nums','add_time']
ordering = ['-click_nums']
readonly_fields = ['click_nums']
exclude = ['fav_nums']
inlines = [LessonInline, CourseResourceInline]
xadmin.site.register(Course, CourseAdmin)
同一个model 后台注册2个管理器,分别管理同一张表!class Course(models.Model):
course_org = models.ForeignKey(CourseOrg, verbose_name=u"课程机构", null=True, blank=True)
name = models.CharField(max_length=50, verbose_name=u"课程名")
desc = models.CharField(max_length=300, verbose_name=u"课程描述")
detail = models.TextField(verbose_name=u"课程详情")
teacher = models.ForeignKey(Teacher, verbose_name=u"讲师", null=True, blank=True)
# TextField不限制max_length
degree = models.CharField(choices=(("cj", u"初级"),("zj", u"中级"),("bj", u"高级")), max_length=2, verbose_name=u"难度")
learn_times = models.IntegerField(default=0, verbose_name=u"学习时长(分钟数)")
students= models.IntegerField(default=0, verbose_name=u"学习人数")
fav_nums= models.IntegerField(default=0, verbose_name=u"收藏人数")
image = models.ImageField(upload_to="courses/%y/%m", verbose_name=u"封面图", max_length=100)
click_nums = models.IntegerField(default=0, verbose_name=u"点击数")
category = models.CharField(default=u"后端开发", max_length=20, verbose_name=u"课程类别")
tag = models.CharField(default="", max_length=10, verbose_name= u"课程标签")
youneed_know = models.CharField(max_length=300, verbose_name=u"课程须知", default="")
teacher_tell = models.CharField(max_length=300 , verbose_name=u"老师告诉你学到什么", default="")
is_banner = models.BooleanField(default=False, verbose_name=u"是否轮播")
add_time = models.DateTimeField(default=datetime.now, verbose_name=u"添加时间")
def get_zj_nums(self):
return self.lesson_set.all().count()
def get_learn_users(self):
return self.usercourse_set.all()[:5]
class Meta:
verbose_name = u"课程"
verbose_name_plural = verbose_name
def __unicode__(self):
return self.name
class BannerCourse(Course):
class Meta:
verbose_name = u"轮播课程"
verbose_name_plural = verbose_name
proxy = True
class CourseAdmin(object):
list_display = ['name','desc','detail','degree','learn_times','students','fav_nums','image','click_nums','add_time']
search_fields = ['name','desc','detail','degree','learn_times','students','fav_nums','image','click_nums']
list_filter = ['name','desc','detail','degree','learn_times','students','fav_nums','image','click_nums','add_time']
ordering = ['-click_nums']
readonly_fields = ['click_nums']
exclude = ['fav_nums']
inlines = [LessonInline, CourseResourceInline]
def queryset(self):
qs = super(CourseAdmin, self).queryset()
qs = qs.filter(is_banner=False)
return qs
xadmin.site.register(Course, CourseAdmin)
class BannerCourseAdmin(object):
list_display = ['name','desc','detail','degree','learn_times','students','fav_nums','image','click_nums','add_time']
search_fields = ['name','desc','detail','degree','learn_times','students','fav_nums','image','click_nums']
list_filter = ['name','desc','detail','degree','learn_times','students','fav_nums','image','click_nums','add_time']
ordering = ['-click_nums']
readonly_fields = ['click_nums']
exclude = ['fav_nums']
inlines = [LessonInline, CourseResourceInline]
def queryset(self):
qs = super(BannerCourseAdmin, self).queryset()
qs = qs.filter(is_banner=True)
return qs
xadmin.site.register(BannerCourse, BannerCourseAdmin)
list_editable
list_editable = ['name','degree']
list中 函数的引用class Course(models.Model):
course_org = models.ForeignKey(CourseOrg, verbose_name=u"课程机构", null=True, blank=True)
name = models.CharField(max_length=50, verbose_name=u"课程名")
desc = models.CharField(max_length=300, verbose_name=u"课程描述")
detail = models.TextField(verbose_name=u"课程详情")
teacher = models.ForeignKey(Teacher, verbose_name=u"讲师", null=True, blank=True)
# TextField不限制max_length
degree = models.CharField(choices=(("cj", u"初级"),("zj", u"中级"),("bj", u"高级")), max_length=2, verbose_name=u"难度")
learn_times = models.IntegerField(default=0, verbose_name=u"学习时长(分钟数)")
students= models.IntegerField(default=0, verbose_name=u"学习人数")
fav_nums= models.IntegerField(default=0, verbose_name=u"收藏人数")
image = models.ImageField(upload_to="courses/%y/%m", verbose_name=u"封面图", max_length=100)
click_nums = models.IntegerField(default=0, verbose_name=u"点击数")
category = models.CharField(default=u"后端开发", max_length=20, verbose_name=u"课程类别")
tag = models.CharField(default="", max_length=10, verbose_name= u"课程标签")
youneed_know = models.CharField(max_length=300, verbose_name=u"课程须知", default="")
teacher_tell = models.CharField(max_length=300 , verbose_name=u"老师告诉你学到什么", default="")
is_banner = models.BooleanField(default=False, verbose_name=u"是否轮播")
add_time = models.DateTimeField(default=datetime.now, verbose_name=u"添加时间")
def get_zj_nums(self):
return self.lesson_set.all().count()
get_zj_nums.short_description = u'章节数'
def get_learn_users(self):
return self.usercourse_set.all()[:5]
class Meta:
verbose_name = u"课程"
verbose_name_plural = verbose_name
def __unicode__(self):
return self.name
class CourseAdmin(object):
list_display = ['name','desc','detail','degree','learn_times','students','fav_nums','image','click_nums','add_time','get_zj_nums']
search_fields = ['name','desc','detail','degree','learn_times','students','fav_nums','image','click_nums']
list_filter = ['name','desc','detail','degree','learn_times','students','fav_nums','image','click_nums','add_time']
ordering = ['-click_nums']
readonly_fields = ['click_nums']
list_editable = ['name','degree']
exclude = ['fav_nums']
inlines = [LessonInline, CourseResourceInline]
自定义html代码def go_to(self):
from django.utils.safestring import mark_safe
return mark_safe('<a href="http://www.baidu.com/{0}" target="_blank">Link</a>'.format(self.name))
go_to.short_description = u'章节数'
list_display = ['name','desc','detail','degree','learn_times','students','fav_nums','image','click_nums','add_time','get_zj_nums','go_to']
定时刷新
refresh_times = [3, 5]
数据发生变更触发操作 |
https://blog.csdn.net/wgpython/article/details/79585205
uedit插件
excel插件
https://blog.csdn.net/win_turn/article/details/55517759
http://django-simple-captcha.readthedocs.io/en/latest/usage.html#installation
http://projectsedu.com/2017/08/15/centos7-%E4%B8%8B%E9%80%9A%E8%BF%87nginx-uwsgi%E9%83%A8%E7%BD%B2django%E5%BA%94%E7%94%A8/