专题:Django2.0.8+xadmin2实现在线学习网站
Django2.0.8+xadmin2实现在线学习网站,课程、讲师、机构、用户收藏功能。GitHub地址:https://github.com/xyliurui/OnlineLearningPlatform ;Django版本:2.0.8
更多内容请点击 我的博客 查看,欢迎来访。
模型设计
- users-用户管理
- course-课程管理
- organization-机构和教师管理
- operation-用户操作管理
用户应用users
点击Tools 菜单下 Run manage.py Task
manage.py@DjangoOnlineLearningPlatform > startapp users
用户models
系统自动生成的user表如下:
- id: 主键, password 密码, last_login Django自动记录用户最后登录时间,。
- is_superuser 表明用户是否是超级用户(后台管理会用到)。
- username 用户名字段不要随便改动, email 邮箱,
- is_staff 表示是否是员工(后台管理会用到)。
- is_active 用户是否是激活状态, date_joined 注册时间。
我们需要扩展我们自己的需求字段,例如昵称,生日,性别等。
既要保留原有字段,邮箱添加新的字段,可以使用AbstractUser
,点击进去就可以看到User原有的字段
编辑users/models.py,添加代码
from django.db import models
from django.contrib.auth.models import AbstractUser
class UserProfile(AbstractUser):
# 自定义的性别选择规则
GENDER_CHOICES = (
('male', '男'),
('female', '女')
)
nick_name = models.CharField(max_length=50, default='', verbose_name='昵称')
birthday = models.DateField(null=True, blank=True, verbose_name='生日')
gender = models.CharField(max_length=10, choices=GENDER_CHOICES, default='male', verbose_name='性别')
address = models.CharField(max_length=100, default='', verbose_name='地址')
mobile = models.CharField(max_length=11, null=True, blank=True, verbose_name='电话')
image = models.ImageField(upload_to='image/%Y/%m', default='image/default.png', max_length=100, blank=True, null=True, verbose_name='头像')
# Meta信息,即后台栏目名
class Meta:
verbose_name_plural = verbose_name = '用户信息'
def __str__(self):
return self.username
点进AbstractUser
可以看到这个models里面就有我们默认表的那些字段。
因为Image字段需要用到pillow所以需要安装该库
(Venv_learning_platform) D:\Apps\Python>pip install pillow -i https://pypi.douban.com/simple
注意:CharField必须有max_length, Imagefield实际也是charfield所以也要有max_length
注册users应用
修改settings.py文件,添加users应用
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users',
]
重载AUTH_USER_MODEL
修改settings.py文件,添加以下代码
# 此处重载是为了使我们的UserProfile生效,相当于使用UserProfile替换User表使用
AUTH_USER_MODEL = "users.UserProfile"
同步users数据库
点击Tools 菜单下 Run manage.py Task
manage.py@DjangoOnlineLearningPlatform > makemigrations users
manage.py@DjangoOnlineLearningPlatform > migrate users
循环引用知识
设计app时每个app都有model
如图:我们在user中定义usercourse记录用户学习的课程。会有两个外键:user和course。
我们就会import Courses.models
如果用户对课程的评论:会放在 Courses.models当中。评论我们需要保存相应的用户。
我们就会import User.models
循环import
会出错。a与b相互调用,造成等待。
解决循环引用: 分层设计
目前已有app:users
、courses
、organization
另外一个app高于这些app的层级。operation.上一层app
可以import下层的app。
邮箱验证码和轮播图models
user中还需要添加的(前提是这些功能比较独立):
- EmailVerifyRecord - 邮箱验证码
- PageBanner - 轮播图
# 第一块区域import官方包
from datetime import datetime
# 第二块区域import第三方包
from django.db import models
from django.contrib.auth.models import AbstractUser
# 第三块区域import自己定义的
class EmailVerifyRecord(models.Model):
SEND_CHOICES = (
("register", "注册"),
("forget", "找回密码")
)
code = models.CharField(max_length=20, verbose_name='验证码')
email = models.EmailField(max_length=50, verbose_name='邮箱')
send_type = models.CharField(choices=SEND_CHOICES, default='register', max_length=10, verbose_name='发送类型')
# 这里的now得去掉(), 不去掉会根据编译时间。而不是根据实例化时间
send_time = models.DateTimeField(default=datetime.now, verbose_name='发送时间')
class Meta:
verbose_name_plural = verbose_name = '邮箱验证码'
def __str__(self):
return '{}({})'.format(self.code, self.email)
# 1、图片 2. 点击图片地址 3. 轮播图序号(控制前后)
class Banner(models.Model):
title = models.CharField(max_length=100, verbose_name='标题')
image = models.ImageField(upload_to='banner/%Y/%m', max_length=100, blank=True, null=True, verbose_name='轮播图')
url = models.URLField(max_length=200, verbose_name='访问地址')
# 默认index很大靠后。想要靠前修改index值。
index = models.IntegerField(default=100, verbose_name='顺序')
add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')
class Meta:
verbose_name_plural = verbose_name = '轮播图'
def __str__(self):
return self.title
同步users数据库
manage.py@DjangoOnlineLearningPlatform > makemigrations users
manage.py@DjangoOnlineLearningPlatform > migrate users
与用户相关的评论啊,点赞啊。学习的课程啊并没有放进来,因为那些独立性不高。
容易产生循环引用。我们把那些放到operation中。
课程应用courses
点击 Tools 菜单下 Run manage.py Task
manage.py@DjangoOnlineLearningPlatform > startapp courses
course中需要那些表:
- 课程本身需要一张表
- 点进去之后点击开始学习
- 课程基本信息需要一张表,章节表与课程表存在(一个课程对应多个章节)
- 章节表中:章节的名称。 章节与视频(一个章节对应多个视频)
课程总体结构: 课程本身---(一对多)--->章节---(一对多)--->视频信息
资源下载放在课程里面的。一个课程对应多个资源
共四张表:
- 课程本身---(一对多)--->章节----(一对多)---->视频信息
- 课程本身---(一对多)---> 资源表
- Course - 课程基本信息
- Lesson - 章节信息
- Video - 视频
- CourseResource - 课程资源
课程信息models
courses.models.py
from django.db import models
from datetime import datetime
# 课程信息表
class Course(models.Model):
DEGREE_CHOICES = (
("cj", "初级"),
("zj", "中级"),
("gj", "高级")
)
name = models.CharField(max_length=50, verbose_name='课程名')
desc = models.CharField(max_length=300, verbose_name='课程描述')
# 后面会改为富文本
detail = models.TextField(verbose_name='课程详情')
degree = models.CharField(max_length=3, choices=DEGREE_CHOICES, verbose_name='课程难度')
learn_times = models.IntegerField(default=0, verbose_name='学习时长(分钟数)')
# 点击开始学习后记录学习人数
students = models.IntegerField(default=0, verbose_name='学习人数')
fav_nums = models.IntegerField(default=0, verbose_name='收藏人数')
image = models.ImageField(upload_to='course/%Y/%m', max_length=100, blank=True, null=True, verbose_name='封面图')
# 点击到课程信息界面即需要记录点击数
click_nums = models.IntegerField(default=0, verbose_name='点击数')
add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')
class Meta:
verbose_name = verbose_name_plural = '课程'
def __str__(self):
return self.name
课程其他资源models
一对多, 多对一都可以使用django的外键来完成。
# 课程章节
class Lesson(models.Model):
# 一个课程下有多个章节,需要用到一对多的关系,使用外键完成
course = models.ForeignKey(Course, on_delete=models.CASCADE, verbose_name='课程')
name = models.CharField(max_length=100, verbose_name='章节名')
add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')
class Meta:
verbose_name_plural = verbose_name = '章节'
def __str__(self):
return '课程 ' + self.course.name + '的章节:' + self.name
# 课程视频
class Video(models.Model):
lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE, verbose_name='章节')
name = models.CharField(max_length=100, verbose_name='视频名')
add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')
class Meta:
verbose_name_plural = verbose_name = '视频'
def __str__(self):
return '章节 ' + self.lesson.name + '的视频:' + self.name
# 课程资源
class CourseResource(models.Model):
course = models.ForeignKey(Course, on_delete=models.CASCADE, verbose_name='课程')
name = models.CharField(max_length=100, verbose_name='名称')
# 下载的是文件,需要用到文件下载FileField,在后台管理中会自动生成文件上传的按钮
download = models.FileField(upload_to='course/resource/%Y/%m', max_length=100, verbose_name='资源文件')
add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')
class Meta:
verbose_name_plural = verbose_name = '课程资源'
def __str__(self):
return '课程 ' + self.course.name + '的资源:' + self.name
同步courses数据库
settings.py添加courses应用
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users',
'courses',
]
执行同步
manage.py@DjangoOnlineLearningPlatform > makemigrations courses
manage.py@DjangoOnlineLearningPlatform > migrate courses
组织机构organization
点击Tools 菜单下 Run manage.py Task
manage.py@DjangoOnlineLearningPlatform > startapp organization
课程是属于机构的, 机构有机构类别,城市等字段。讲师实体。
我要学习的提交表单会与用户关联,存放在机构。
- CourseOrg - 课程机构基本信息
- Teacher - 教师基本信息
- CityDict - 城市信息
课程机构models
其中课程数,学习人数可以动态统计。机构地址,机构经典课程。
organization/models.py
from django.db import models
from datetime import datetime
# 城市信息,用户课程机构所在城市选择
class CityDict(models.Model):
name = models.CharField(max_length=20, verbose_name='城市')
desc = models.TextField(verbose_name='描述')
add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')
class Meta:
verbose_name_plural= verbose_name = '城市'
def __str__(self):
return self.name
# 课程机构信息
class CourseOrg(models.Model):
name = models.CharField(max_length=50, verbose_name='机构名称')
desc = models.TextField(verbose_name='机构描述')
click_nums = models.IntegerField(default=0, verbose_name='点击数')
fav_nums = models.IntegerField(default=0, verbose_name='收藏数')
image = models.ImageField(upload_to='org/%Y/%m', max_length=100, blank=True, null=True, verbose_name='封面图')
address = models.CharField(max_length=150, verbose_name='机构地址')
city = models.ForeignKey(CityDict, on_delete=models.CASCADE, verbose_name='所在城市')
add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')
class Meta:
verbose_name_plural = verbose_name = '课程机构'
def __str__(self):
return self.name
# 机构里的讲师信息
class Teacher(models.Model):
org = models.ForeignKey(CourseOrg, on_delete=models.CASCADE, related_name='teachers', verbose_name='所属机构')
name = models.CharField(max_length=50, verbose_name='讲师名称')
work_years = models.IntegerField(default=0, verbose_name='工作年限')
work_company = models.CharField(max_length=50, verbose_name='就职公司')
work_position = models.CharField(max_length=50, verbose_name='公司职位')
points = models.CharField(max_length=50, verbose_name=u"教学特点")
click_nums = models.IntegerField(default=0, verbose_name=u"点击数")
fav_nums = models.IntegerField(default=0, verbose_name=u"收藏数")
add_time = models.DateTimeField(default=datetime.now, verbose_name=u"添加时间")
class Meta:
verbose_name_plural = verbose_name = '讲师'
def __str__(self):
return self.name
同步organization数据库
settings.py添加organization应用
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users',
'courses',
'organization',
]
同步数据库
manage.py@DjangoOnlineLearningPlatform > makemigrations organization
manage.py@DjangoOnlineLearningPlatform > migrate organization
用户操作operation
分析需要那些表:
- 用户可以提交我要学习的个人需求。
- 学员的课程评论信息
- 收藏:可以收藏公开课, 授课讲师, 授课机构, 用户消息提醒。
- 个人中心:我的课程说明用户和课程之间的学习关系也需要保存。
模型设计
- UserAsk - 用户咨询
- CourseComments - 用户评论
- UserFavorite - 用户收藏
- UserMessage - 用户消息
- UserCourses- 用户学习的课程(用户点击我要学习记录的关联表)
点击Tools 菜单下 Run manage.py Task
manage.py@DjangoOnlineLearningPlatform > startapp operation
用户操作models
from datetime import datetime
from django.db import models
from users.models import UserProfile
from courses.models import Course
# 用户我要学习表单
class UserAsk(models.Model):
name = models.CharField(max_length=20, verbose_name='姓名')
mobile = models.CharField(max_length=11, verbose_name='手机号')
course_name = models.CharField(max_length=50, verbose_name='课程名')
add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')
class Meta:
verbose_name_plural = verbose_name = '用户咨询'
def __str__(self):
return self.name + ' 咨询 ' + self.course_name
# 用户对于课程评论
class CourseComments(models.Model):
# 会涉及两个外键: 1. 用户, 2. 课程。import进来
course = models.ForeignKey(Course, on_delete=models.CASCADE, verbose_name='课程')
user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, verbose_name='用户')
comments = models.CharField(max_length=300, verbose_name='评论')
add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')
class Meta:
verbose_name_plural = verbose_name = '课程评论'
def __str__(self):
return self.user.username + ' 评论' + self.course.name
# 用户对于课程,机构,讲师的收藏
class UserFavorite(models.Model):
# 会涉及四个外键。用户,课程,机构,讲师import
TYPE_CHOICES = (
(1, "课程"),
(2, "课程机构"),
(3, "讲师")
)
user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, verbose_name='用户')
# 表明收藏的数据id
fav_id = models.IntegerField(default=0, verbose_name='数据id')
fav_type = models.IntegerField(choices=TYPE_CHOICES, default=1, verbose_name='收藏类型')
add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')
class Meta:
verbose_name_plural = verbose_name = '用户收藏'
def __str__(self):
return self.user.username + ' 收藏'
# 用户消息
class UserMessage(models.Model):
# 因为我们的消息有两种:发给全员和发给某一个用户。
# 所以如果使用外键,每个消息会对应要有用户。很难实现全员消息。
# 为0发给所有用户,不为0就是发给用户的id
user = models.IntegerField(default=0, verbose_name="接收用户")
message = models.CharField(max_length=500, verbose_name="消息内容")
has_read = models.BooleanField(default=False, verbose_name="是否已读")
add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")
class Meta:
verbose_name_plural = verbose_name = '用户消息'
def __str__(self):
return self.message
# 用户学习课程
class UserCourse(models.Model):
# 会涉及两个外键: 1. 用户, 2. 课程。import进来
course = models.ForeignKey(Course, on_delete=models.CASCADE, verbose_name="课程")
user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, verbose_name="用户")
add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")
class Meta:
verbose_name = u"用户课程"
verbose_name_plural = verbose_name
def __str__(self):
return self.user.username + ' 学习 ' + self.course.name
同步operation数据库
settings.py添加operation
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users',
'courses',
'organization',
'operation',
]
执行同步
manage.py@DjangoOnlineLearningPlatform > makemigrations operation
manage.py@DjangoOnlineLearningPlatform > migrate operation
Django2 on_delete配置
TypeError: __init__() missing 1 required positional argument: 'on_delete'
这是因为在2中,外键关系必须指明删除时的操作。
比如:出租车都归属于出租车公司。如果出租车公司倒闭了,那这些汽车该怎么处理。
必须自己指明: 我觉得可以直接进行级联删除。
django提供了:
- CASCADE
- PROTECT
- SET_NULL
- SET_DEFAULT
等选项。选择了CASCADE删除。
将(dajngo 2)项目中所有的外键修改为如下面代码所示:
也就是添加了on_delete=models.CASCADE
使其级联删除。
App集中管理
在项目下新建Python的package: apps
把四个app都拖进apps中去。
去掉searchfor的勾选。拖进去之后会报错,说找不到那些import的模块了。
解决方案:在apps上右键Mark Directory as为sourceRoot。根目录下找不到的,会去apps下搜索。
但是这时候cmd下运行python manage runserver xxx还是会报错。
将apps添加到python搜索目录下,修改settings.py
import os
import sys
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(BASE_DIR, 'apps')) # 添加apps搜索路径
设置静态资源和文件上传路径
设置静态资源路径
settings.py增加
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
# STATIC_ROOT = os.path.join(BASE_DIR, 'static')
设置文件上传路径
# 设置我们上传文件的路径
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
修改项目中urls.py文件
from django.contrib import admin
from django.urls import path
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
这样,在后台才会正常返回图片或文件的链接。