Django+xadmin 打造线上教育平台
一、开发环境搭建
1、开发环境:
- python:3.6
- Django:2.0.2
后台管理:
xadmin
2、虚拟环境及项目相关安装:
- virtualenv安装及虚拟环境搭建
- pycharm、Navicat、python的安装
环境搭建不再累述,详情参参考:https://www.cnblogs.com/Eric15/articles/9517232.html
3、项目简介
系统功能概况:
- 系统具有完整的用户登录注册以及找回密码功能,拥有完整个人中心。
- 个人中心: 修改头像,修改密码,修改邮箱,可以看到我的课程以及我的收藏。可以删除收藏,我的消息。
- 导航栏: 公开课,授课讲师,授课机构,全局搜索。
- 点击
公开课
–> 课程列表,排序-搜索。热门课程推荐,课程的分页。 - 点击
课程
–> 课程详情页中对课程进行收藏,取消收藏。富文本展示课程内容。 - 点击
开始学习
–> 课程的章节信息,课程的评论信息。课程资源的下载链接。 - 点击
授课讲师
–>授课讲师列表页,对讲师进行人气排序以及分页,右边有讲师排行榜。 - 点击
讲师的详情页面
–> 对讲师进行收藏和分享,以及讲师的全部课程。 - 导航栏: 授课机构有分页,排序筛选功能。
- 机构列表页右侧有快速提交我要学习的表单。
- 点击
机构
–> 左侧:机构首页,机构课程,机构介绍,机构讲师。 - 后台管理系统可以
切换主题
。左侧每一个功能都有列表显示, 增删改查,筛选功能。 - 课程列表页可以对不同字段进行排序。选择多条记录进行删除操作。
- 课程列表页:过滤器->选择字段范围等,搜索,导出csv,xml,json。
- 课程新增页面上传图片,富文本的编辑。时间选择,添加章节,添加课程资源。
- 日志记录:记录后台人员的操作
二、新建项目:MxOnline
示例:
1)首先打开终端,创建虚拟环境:
mkvirtualenv mxonline
2)djangoTest虚拟环境中安装django版本:
pip install django==2.0.2
3)打开pycharm,新建python-django项目
三、数据库更换
pycharm新建项目,默认用的是自带ORM数据库(sqlite),将其改成使用mysql数据库
1)setting.py中更改数据库配置:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'MxOnline', #数据库名字 'USER': 'root', #账号 'PASSWORD': '******', #密码 'HOST': '127.0.0.1', #IP 'PORT': '3306', #端口 } }
2)Navicat新建数据库:
3)虚拟环境下安装MySQLclient:
一般直接安装:pip install mysqlclient ,
但有可能报错:Failed building wheel for mysqlclient
此时,需要用另一种方式安装:
- 访问:https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted ,找到mysqlclient相关
- 下载:mysqlclient‑1.3.13‑cp36‑cp36m‑win_amd64.whl ,我的是win 64位 python3.6的,故下载这个
- 进入上述文件所在的地方,在终端虚拟环境中安装:pip install mysqlclient‑1.3.13‑cp36‑cp36m‑win_amd64.whl ,即可
四、model设计
1、生成四个app:users 、 course 、 organization 、 operation
python manage.py startapp users # 用户信息 python manage.py startapp course # 课程信息 python manage.py startapp organization # 讲师及授课机构相关 python manage.py startapp operation # 用户管理操作
在项目MxOnline中新建apps包,用于放app,将上述四个app放进apps内
2、再创建一个extra_apps包 ,表示存放的是第三方的源码包
3、在setting中配置:
import sys sys.path.insert(0,os.path.join(BASE_DIR,'apps')) sys.path.insert(0,os.path.join(BASE_DIR, 'extra_apps'))
4、将4个app注册到 INSTALLED_APPS中:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'users', 'course', 'organization', 'operation', ]
另外需要创建静态文件夹:
- static:用于存放静态文件,css、js等等
- media:用户上传文件及图片路径
setting中配置静态文件路径:
# 静态文件路径 STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR,'static'), ] # 设置上传文件的路径 MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR,'media')
此时,项目中文件情况:
现在开始,设计四个app的表结构:
1、users中的models.py表:
from django.db import models from django.contrib.auth.models import AbstractUser # Create your models here. class UserProfile(AbstractUser): gender_choices = ( ('male','男'), ('female','女') ) nick_name = models.CharField('昵称',max_length=32 ,default='') birthday = models.DateField('生日',null=True,blank=True) gender = models.CharField('性别',max_length=5,choices=gender_choices,default='female') adress = models.CharField('地址',max_length=100,default='') mobile = models.CharField('手机号',max_length=11,null=True,blank=True) image = models.ImageField(upload_to='image/%Y%m',default='image/default.png',max_length=100)
email = models.EmailField('邮箱',blank=True,unique=True) # 重写email,加上'唯一'标识 ,原因在于django中AbstractUser的email字段没设置唯一,容易造成多个用户邮箱相同 class Meta: verbose_name = '用户信息' verbose_name_plural = verbose_name def __str__(self): return self.username
表中用到ImageField,该字段需要用到pillow库,我们需要安装它:
报错:
pip install pillow # 安装pillow
UserProfile继承于django自带的 AbstractUser,需要重新配置 AUTH_USER_MODEL的重载:
AUTH_USER_MODEL = 'users.UserProfile'
在users/models.py中继续添加 EmailVerifyRecord(邮箱验证)类、Banner(轮播图)类:
class EmailVerifyRecord(models.Model): """邮箱验证""" send_choices = ( ('register','注册'), ('forget','找回密码'), ('update_email','修改邮箱') ) code = models.CharField('验证码',max_length=10) email = models.EmailField('邮箱',max_length=50) send_type = models.CharField(choices=send_choices,max_length=30) send_time = models.DateTimeField(default=datetime.now) class Meta: verbose_name = '邮箱验证码' verbose_name_plural = verbose_name def __str__(self): return self.code class Banner(models.Model): """轮播图""" title = models.CharField('标题',max_length=100) image = models.ImageField('轮播图',upload_to='banner/%Y%m',max_length=100) url = models.URLField('访问地址',max_length=200) index = models.IntegerField('顺序',default=100) add_time = models.DateTimeField('添加时间',default=datetime.now) class Meta: verbose_name = '轮播图' verbose_name_plural = verbose_name def __str__(self): return self.title
2、course/views.py表设计:
from django.db import models from datetime import datetime # Create your models here. class Course(models.Model): '''课程''' DEGREE_CHOICES = ( ("cj", "初级"), ("zj", "中级"), ("gj", "高级"), ) name = models.CharField("课程名",max_length=50) desc = models.CharField("课程描述",max_length=300) detail = models.TextField("课程详情") # detail = UEditorField(verbose_name=u'课程详情', width=600, height=300, imagePath="courses/ueditor/", # filePath="courses/ueditor/", default='') degree = models.CharField('难度',choices=DEGREE_CHOICES, max_length=2) learn_times = models.IntegerField("学习时长(分钟数)",default=0) students = models.IntegerField("学习人数",default=0) fav_nums = models.IntegerField("收藏人数",default=0) image = models.ImageField("封面图",upload_to="courses/%Y/%m",max_length=100) click_nums = models.IntegerField("点击数",default=0) tag = models.CharField('课程标签',default='',max_length=10) is_banner = models.BooleanField('是否轮播',default=False) add_time = models.DateTimeField("添加时间",default=datetime.now,) # course_org = models.ForeignKey(CourseOrg, on_delete=models.CASCADE, verbose_name="所属机构", null=True, blank=True) category = models.CharField("课程类别",max_length=20, default="") # teacher = models.ForeignKey(Teacher,verbose_name='讲师',null=True,blank=True,on_delete=models.CASCADE) youneed_know = models.CharField('课程须知',max_length=300,default='') teacher_tell = models.CharField('老师告诉你',max_length=300,default='') class Meta: verbose_name = "课程" verbose_name_plural = verbose_name def __str__(self): return self.name class Lesson(models.Model): '''课程章节''' course = models.ForeignKey(Course,verbose_name='课程',on_delete=models.CASCADE) name = models.CharField("章节名",max_length=100) add_time = models.DateTimeField("添加时间",default=datetime.now) class Meta: verbose_name = "章节" verbose_name_plural = verbose_name unique_together = ('course','name') def __str__(self): return "%s第%s章"% (self.course,self.name) class Video(models.Model): '''章节视频''' lesson = models.ForeignKey(Lesson, verbose_name="章节",on_delete=models.CASCADE) name = models.CharField("视频名",max_length=100) # url = models.CharField('访问地址',default='',max_length=200) # learn_times = models.IntegerField("学习时长(分钟数)", default=0) add_time = models.DateTimeField("添加时间", default=datetime.now) class Meta: verbose_name = "视频" verbose_name_plural = verbose_name def __str__(self): return self.name class CourseResource(models.Model): """课程资源""" course = models.ForeignKey(Course, verbose_name="课程",on_delete=models.CASCADE) name = models.CharField("名称",max_length=100) download = models.FileField("资源文件",upload_to="course/resource/%Y/%m",max_length=100) add_time = models.DateTimeField("添加时间", default=datetime.now) class Meta: verbose_name = "课程资源" verbose_name_plural = verbose_name def __str__(self): return self.name
3、organization/views.py表设计:
from datetime import datetime from django.db import models class CityDict(models.Model): """城市""" name = models.CharField('城市',max_length=20) desc = models.CharField('描述',max_length=200) add_time = models.DateTimeField(default=datetime.now) class Meta: verbose_name = '城市' verbose_name_plural= verbose_name def __str__(self): return self.name class CourseOrg(models.Model): """培训机构""" ORG_CHOICES = ( ("pxjg", "培训机构"), ("gx", "高校"), ("gr", "个人"), ) name = models.CharField('机构名称',max_length=50) desc = models.TextField('机构描述') category = models.CharField(max_length=20, choices=ORG_CHOICES, verbose_name="机构类别", default="mx") click_nums = models.IntegerField('点击数',default=0) tag = models.CharField('机构标签',max_length=10,default='全国知名') fav_nums = models.IntegerField('收藏数',default=0) students = models.IntegerField("学习人数",default=0) course_nums = models.IntegerField("课程数",default=0) image = models.ImageField('logo',upload_to='org/%Y/%m',max_length=100) address = models.CharField('机构地址',max_length=150,) city = models.ForeignKey(CityDict,verbose_name='所在城市',on_delete=models.CASCADE) add_time = models.DateTimeField(default=datetime.now) class Meta: verbose_name = '课程机构' verbose_name_plural = verbose_name def get_teacher_nums(self): #获取机构的教师数 return self.teacher_set.all().count() def __str__(self): return self.name class Teacher(models.Model): """课程讲师""" org = models.ForeignKey(CourseOrg,verbose_name='所属机构',on_delete=models.CASCADE) name = models.CharField('教师名',max_length=50) work_years = models.IntegerField('工作年限',default=0) work_company = models.CharField('就职公司',max_length=50) work_position = models.CharField('公司职位',max_length=50) points = models.CharField('教学特点',max_length=50) click_nums = models.IntegerField('点击数',default=0) fav_nums = models.IntegerField('收藏数',default=0) # teacher_age = models.IntegerField('年龄',default=25) image = models.ImageField( default='', upload_to="teacher/%Y/%m", verbose_name="头像", max_length=100) add_time = models.DateTimeField(default=datetime.now) class Meta: verbose_name = '教师' verbose_name_plural = verbose_name def __str__(self): return "[{0}]的教师: {1}".format(self.org, self.name) def get_course_nums(self): return self.course_set.all().count()
4、operation/views.py表设计:
from datetime import datetime from django.db import models from course.models import Course from users.models import UserProfile class UserAsk(models.Model): '''用户咨询''' name = models.CharField('姓名',max_length=20) mobile = models.CharField('手机',max_length=11) course_name = models.CharField('课程名',max_length=50) add_time = models.DateTimeField('添加时间',default=datetime.now) class Meta: verbose_name = '用户咨询' verbose_name_plural = verbose_name def __str__(self): return self.name class CourseComments(models.Model): '''课程评论''' user = models.ForeignKey(UserProfile,verbose_name='用户',on_delete=models.CASCADE) course = models.ForeignKey(Course,verbose_name='课程',on_delete=models.CASCADE) comments = models.CharField('评论',max_length=200) add_time = models.DateTimeField('添加时间', default=datetime.now) class Meta: verbose_name = '课程评论' verbose_name_plural = verbose_name class UserFavorite(models.Model): '''用户收藏''' FAV_TYPE = ( (1,'课程'), (2,'课程机构'), (3,'讲师') ) user = models.ForeignKey(UserProfile,verbose_name='用户',on_delete=models.CASCADE) fav_id = models.IntegerField('数据id',default=0) fav_type = models.IntegerField(verbose_name='收藏类型',choices=FAV_TYPE,default=1) add_time = models.DateTimeField('添加时间', default=datetime.now) class Meta: verbose_name = '用户收藏' verbose_name_plural = verbose_name class UserMessage(models.Model): user = models.IntegerField('接收用户',default=0) message = models.CharField('消息内容',max_length=500) has_read = models.BooleanField('是否已读',default=False) add_time = models.DateTimeField('添加时间', default=datetime.now) class Meta: verbose_name = '用户消息' verbose_name_plural = verbose_name class UserCourse(models.Model): '''用户课程''' user = models.ForeignKey(UserProfile,verbose_name='用户',on_delete=models.CASCADE) course = models.ForeignKey(Course,verbose_name='课程',on_delete=models.CASCADE) add_time = models.DateTimeField('添加时间', default=datetime.now) class Meta: verbose_name = '用户课程' verbose_name_plural = verbose_name def __str__(self): return self.course.name
至此,4个app的表设计即完成了,目前没考虑到的,之后再补上
五、通过xadmin快速搭建后台管理系统
5.1、xadmin安装和配置
1、xadmin安装:两种方式安装
1)虚拟环境下,直接:pip install xadmin ,会连xadmin依赖的包也都安装好
2)源码安装(推荐):
GitHub上有很多开源项资源,如果想获取某个资源,可以到GitHub上找:https://github.com/
从GitHub上下载xadmin,然后复制放到项目中。本项目将xadmin放到extra_apps包中,
从源码下载直接复制到项目中的方式,需要自己安装xadmin依赖包:https://github.com/sshwsfc/xadmin
- django
- django-crispy-forms
- django-reversion
- django-formtools
- django-import-export
- future
- httplib2
- six
- xlwt
- xlsxwriter # xlwt跟xlsxwriter,不是依赖包,主要用来做ex文件导出,使xadmin功能更加强大些
pip install django-crispy-forms django-reversion django-formtools future httplib2 six django-import-export # 多个依赖包一起安装 pip install xlwt xlsxwriter
2、xadmin的配置:
1)在在setting.py中的INSTALLED_APPS中注册:
INSTALLED_APPS = [ … 'xadmin', #新增 'crispy_forms', #新增 ]
2)在url.py中进行路由配置:
# urls.py from django.urls import path import xadmin urlpatterns = [ path('xadmin/', xadmin.site.urls), ]
3、app注册后,更新表mk生成数据:
python manage.py makemigrations
python manage.py migrate
4、创建超级管理员,访问后台:http://127.0.0.1:8000/xadmin
python manage.py createsuperuser
此时,页面效果:xadmin成功引人使用!
5.2、app中model的注册
1)在users下面创建adminx.py,代码如下:
# users/adminx.py import xadmin from .models import EmailVerifyRecord #xadmin中这里是继承object,不再是继承admin class EmailVerifyRecordAdmin(object): pass xadmin.site.register(EmailVerifyRecord,EmailVerifyRecordAdmin)
2)完善功能,增加显示字段、过滤、搜索等
# users/adminx.py import xadmin from .models import EmailVerifyRecord #xadmin中这里是继承object,不再是继承admin class EmailVerifyRecordAdmin(object): # 显示的列 list_display = ['code', 'email', 'send_type', 'send_time'] # 搜索的字段,不要添加时间搜索 search_fields = ['code', 'email', 'send_type'] # 过滤 list_filter = ['code', 'email', 'send_type', 'send_time'] xadmin.site.register(EmailVerifyRecord,EmailVerifyRecordAdmin)
将剩余三个 app也进行上述操作,最终代码:
1)users/adminx.py:
import xadmin from .models import EmailVerifyRecord,Banner #xadmin中这里是继承object,不再是继承admin class EmailVerifyRecordAdmin(object): # 显示的列 list_display = ['code', 'email', 'send_type', 'send_time'] # 搜索的字段 search_fields = ['code', 'email', 'send_type'] # 过滤 list_filter = ['code', 'email', 'send_type', 'send_time'] class BannerAdmin(object): list_display = ['title', 'image', 'url','index', 'add_time'] search_fields = ['title', 'image', 'url','index'] list_filter = ['title', 'image', 'url','index', 'add_time'] xadmin.site.register(EmailVerifyRecord,EmailVerifyRecordAdmin) xadmin.site.register(Banner,BannerAdmin)
2)course/adminx.py:
# course/adminx.py import xadmin from .models import Course, Lesson, Video, CourseResource # Course的admin管理器 class CourseAdmin(object): '''课程''' list_display = [ 'name','desc','detail','degree','learn_times','students'] search_fields = ['name', 'desc', 'detail', 'degree', 'students'] list_filter = [ 'name','desc','detail','degree','learn_times','students'] class LessonAdmin(object): '''章节''' list_display = ['course', 'name', 'add_time'] search_fields = ['course', 'name'] #这里course__name是根据课程名称过滤 list_filter = ['course__name', 'name', 'add_time'] class VideoAdmin(object): '''视频''' list_display = ['lesson', 'name', 'add_time'] search_fields = ['lesson', 'name'] list_filter = ['lesson', 'name', 'add_time'] class CourseResourceAdmin(object): '''课程资源''' list_display = ['course', 'name', 'download', 'add_time'] search_fields = ['course', 'name', 'download'] list_filter = ['course__name', 'name', 'download', 'add_time'] # 将管理器与model进行注册关联 xadmin.site.register(Course, CourseAdmin) xadmin.site.register(Lesson, LessonAdmin) xadmin.site.register(Video, VideoAdmin) xadmin.site.register(CourseResource, CourseResourceAdmin)
3)organization/adminx.py:
# organization/adminx.py import xadmin from .models import CityDict, CourseOrg, Teacher class CityDictAdmin(object): '''城市''' list_display = ['name', 'desc', 'add_time'] search_fields = ['name', 'desc'] list_filter = ['name', 'desc', 'add_time'] class CourseOrgAdmin(object): '''机构''' list_display = ['name', 'desc', 'click_nums', 'fav_nums','add_time' ] search_fields = ['name', 'desc', 'click_nums', 'fav_nums'] list_filter = ['name', 'desc', 'click_nums', 'fav_nums','city__name','address','add_time'] class TeacherAdmin(object): '''老师''' list_display = [ 'name','org', 'work_years', 'work_company','add_time'] search_fields = ['org', 'name', 'work_years', 'work_company'] list_filter = ['org__name', 'name', 'work_years', 'work_company','click_nums', 'fav_nums', 'add_time'] xadmin.site.register(CityDict, CityDictAdmin) xadmin.site.register(CourseOrg, CourseOrgAdmin) xadmin.site.register(Teacher, TeacherAdmin)
4)operation/adminx.py:
# operation/adminx.py import xadmin from .models import UserAsk, UserCourse, UserMessage, CourseComments, UserFavorite class UserAskAdmin(object): '''用户表单我要学习''' list_display = ['name', 'mobile', 'course_name', 'add_time'] search_fields = ['name', 'mobile', 'course_name'] list_filter = ['name', 'mobile', 'course_name', 'add_time'] # class UserCourseAdmin(object): '''用户课程学习''' list_display = ['user', 'course', 'add_time'] search_fields = ['user', 'course'] list_filter = ['user', 'course', 'add_time'] class UserMessageAdmin(object): '''用户消息后台''' list_display = ['user', 'message', 'has_read', 'add_time'] search_fields = ['user', 'message', 'has_read'] list_filter = ['user', 'message', 'has_read', 'add_time'] class CourseCommentsAdmin(object): '''用户评论后台''' list_display = ['user', 'course', 'comments', 'add_time'] search_fields = ['user', 'course', 'comments'] list_filter = ['user', 'course', 'comments', 'add_time'] class UserFavoriteAdmin(object): '''用户收藏后台''' list_display = ['user', 'fav_id', 'fav_type', 'add_time'] search_fields = ['user', 'fav_id', 'fav_type'] list_filter = ['user', 'fav_id', 'fav_type', 'add_time'] # 将后台管理器与models进行关联注册。 xadmin.site.register(UserAsk, UserAskAdmin) xadmin.site.register(UserCourse, UserCourseAdmin) xadmin.site.register(UserMessage, UserMessageAdmin) xadmin.site.register(CourseComments, CourseCommentsAdmin) xadmin.site.register(UserFavorite, UserFavoriteAdmin)
5.3、xadmin的全局配置 (全局配置,放在users下的adminx.py文件中)
1)主题功能:
from xadmin import views # 创建xadmin的最基本管理器配置,并与view绑定 class BaseSetting(object): # 开启主题功能 enable_themes = True use_bootswatch = True # 将基本配置管理与view绑定 xadmin.site.register(views.BaseAdminView,BaseSetting)
2)全局配置:
- 系统标题
- footer信息
- 菜单收起/展开
# 全局修改,固定写法 class GlobalSettings(object): # 修改title site_title = '在线教育后台管理系统' # 修改footer site_footer = '在线教育网' # 菜单可收起/展开 menu_style = 'accordion' # 将title和footer信息进行注册 xadmin.site.register(views.CommAdminView,GlobalSettings)
3)将app名称从英文改为中文:
第一步,在各app下的apps.py中,比如users/apps.py 添加:
verbose_name = '用户'
from django.apps import AppConfig class UsersConfig(AppConfig): name = 'users' verbose_name = '用户' # 新增
第二步,有两种方式:
1、在同app下的init.py文件中,如users/__init__.py中添加下面一行代码,即可实现app名称中文化
default_app_config = 'users.apps.UsersConfig'
2、直接在setting中的INSTALLED_APPS中配置,即可实现app名称中文化
INSTALLED_APPS = [ …… 'apps.users.apps.UsersConfig', 'apps.organization.apps.OrganizationConfig', 'apps.operation.apps.OperationConfig', 'apps.course.apps.CourseConfig', # 'users', # 'operation', # 'organization', # 'course', …… ]