课程主页之课程表分析
一、课程表分析
class Course(models.Model):
name = models.CharField(max_length=64)
title = models.CharField(max_length=64)
detail = models.TextField() # 可以关联详情表
type = models.IntegerField(choices=((0, 'Python'), (1, 'Linux')), default=0)
is_show = models.BooleanField(default=False)
# 1. 重点: choices字段后期优化方法: 将choices字段制作成一对多外键关联表, 为了之后的可扩展性埋下伏笔
# level = models.IntegerField(choices=((0, '入门'), (1, '进阶')), default=0)
level = models.ForeignKey(to='LevelChoices', on_delete=models.DO_NOTHING, db_constraint=False)
# 2. 重点: 针对后期需要获取学生学习人数, 课程总时长, 这些需要通过跨表查询才可以实现这总统计, 那么我们使用数据库设计优化普通字段. (注意: 默认值都是0)
students = models.IntegerField(default=0, verbose_name='总学生')
time = models.IntegerField(default=0, verbose_name='总学时')
class Meta:
abstract = True
class LevelChoices(models.Model):
"""choices字段后期优化方法"""
introduction = models.CharField(verbose_name='入门')
advance = models.CharField(verbose_name='进阶')
# 免费课
class FreeCourse(Course):
image = models.ImageField(upload_to='course/free')
attachment = models.FileField(upload_to='attachment')
# 实战课
class ActualCourse(Course):
image = models.ImageField(upload_to='course/actual')
price = models.DecimalField(max_digits=7, decimal_places=2)
cost = models.DecimalField(max_digits=7, decimal_places=2)
# 轻课
class LightCourse(Course):
image = models.ImageField(upload_to='course/light')
price = models.DecimalField(max_digits=7, decimal_places=2)
cost = models.DecimalField(max_digits=7, decimal_places=2)
period = models.IntegerField(verbose_name='学习建议周期(month)', default=0)
# 评论表:分三个表、(id,ctx,date,user_id,free_course_id, comment_id)
# 老师表:在课程表建立多对一外键
# 章节表:在章节表建立多对一外键关联课程
二、免费课案例
1. 创建models:course/models.py
from django.db import models
################################ 基表 ##############################
class BaseModel(models.Model):
# create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
# update_time = models.DateTimeField(auto_now=True, verbose_name='最后更新时间')
# display_order = models.IntegerField()
created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
updated_time = models.DateTimeField(auto_now=True, verbose_name='最后更新时间')
orders = models.IntegerField()
is_delete = models.BooleanField(default=False, verbose_name='是否删除')
is_show = models.BooleanField(default=True, verbose_name='是否展示')
class Meta:
abstract = True
############################ 课程分类表 ##############################
class CourseCategory(BaseModel):
"""
课程
跟课程是一(分类)对多(课程)关系,关系字段写在多的一方(课程)
跟课程是一(老师)对多(课程)关系,关系字段写在多的一方(课程)
跟课程是一(课程)对多(章节)关系,关联字段写在多的一方(章节)
"""
name = models.CharField(max_length=64, unique=True, verbose_name="分类名称")
class Meta:
db_table = "luffy_course_category"
verbose_name = "分类"
verbose_name_plural = verbose_name
def __str__(self):
return "%s" % self.name
class Course(BaseModel):
"""课程"""
course_type = (
(0, '付费'),
(1, 'VIP专享'),
(2, '学位课程')
)
level_choices = (
(0, '初级'),
(1, '中级'),
(2, '高级'),
)
status_choices = (
(0, '上线'),
(1, '下线'),
(2, '预上线'),
)
name = models.CharField(max_length=128, verbose_name="课程名称")
course_img = models.ImageField(upload_to="courses", max_length=255, verbose_name="封面图片", blank=True, null=True)
course_type = models.SmallIntegerField(choices=course_type, default=0, verbose_name="付费类型")
# 使用这个字段的原因
brief = models.TextField(max_length=2048, verbose_name="详情介绍", null=True, blank=True)
level = models.SmallIntegerField(choices=level_choices, default=0, verbose_name="难度等级")
pub_date = models.DateField(verbose_name="发布日期", auto_now_add=True)
period = models.IntegerField(verbose_name="建议学习周期(day)", default=7)
attachment_path = models.FileField(upload_to="attachment", max_length=128, verbose_name="课件路径", blank=True,
null=True)
status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="课程状态")
price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程原价", default=0)
# !!!重点!!!: 数据库设计优化普通字段. (注意: 默认值都是0)
'''
虽然下述的三个字段可以从其他表里面跨表查询计算得出,但是频繁跨表效率, 因此我们建立普通字段,
'''
students = models.IntegerField(verbose_name="学习人数", default=0)
sections = models.IntegerField(verbose_name="总课时数量", default=0)
pub_sections = models.IntegerField(verbose_name="课时更新数量", default=0)
# !!!重点!!!: 一个分类可以有多门课程. 一门课程不可以属于多门分类. 关系: 课程和分类, 课程是多的一方 -> 一对多
course_category = models.ForeignKey("CourseCategory", on_delete=models.SET_NULL, db_constraint=False, null=True,
blank=True,
verbose_name="课程分类")
# !!!重点!!!: 一个老师教一门课程. 一个课程不可以被多个老师教. 关系: 课程和老师, 课程是多的一方. -> 一对多
teacher = models.ForeignKey("Teacher", on_delete=models.DO_NOTHING, null=True, blank=True, db_constraint=False,
verbose_name="授课老师")
class Meta:
db_table = "luffy_course"
verbose_name = "课程"
verbose_name_plural = "课程"
def __str__(self):
return "%s" % self.name
############################# 课程表 ################################
class Teacher(BaseModel):
"""
导师
跟课程是一(老师)对多(课程)关系,关系字段写在多的一方(课程)
一个老师可以讲多门课程
"""
role_choices = (
(0, '讲师'),
(1, '导师'),
(2, '班主任'),
)
name = models.CharField(max_length=32, verbose_name="导师名")
role = models.SmallIntegerField(choices=role_choices, default=0, verbose_name="导师身份")
title = models.CharField(max_length=64, verbose_name="职位、职称")
signature = models.CharField(max_length=255, verbose_name="导师签名", help_text="导师签名", blank=True, null=True)
image = models.ImageField(upload_to="teacher", null=True, verbose_name="导师封面")
brief = models.TextField(max_length=1024, verbose_name="导师描述")
class Meta:
db_table = "luffy_teacher"
verbose_name = "导师"
verbose_name_plural = verbose_name
def __str__(self):
return "%s" % self.name
############################# 章节表 ################################
class CourseChapter(BaseModel):
"""
章节
跟课程是一(课程)对多(章节)关系,关联字段写在多的一方(章节)
一门课程可以有多个章节
"""
chapter = models.SmallIntegerField(verbose_name="第几章", default=1)
name = models.CharField(max_length=128, verbose_name="章节标题")
summary = models.TextField(verbose_name="章节介绍", blank=True, null=True)
pub_date = models.DateField(verbose_name="发布日期", auto_now_add=True)
# !!!重点!!!: related_name的指定争对子查询的反向查询. 当结果有多个的时候就不需要_set. 如:
'''
course = Course.objects.filter(pk=1).first()
res = course.coursechapters.all()
# 如果没有指定related_name那么就会是如下.
res = course.coursechapter_set.all()
'''
# !!!重点!!!: 一个课程可以有多个章节. 一个章节不能在多个课程中. 关系: 课程和课程章节, 章节是多的一方. -> 一对多
course = models.ForeignKey("Course", related_name='coursechapters', on_delete=models.CASCADE, db_constraint=False,
verbose_name="课程名称")
class Meta:
db_table = "luffy_course_chapter"
verbose_name = "章节"
verbose_name_plural = verbose_name
def __str__(self):
return "%s:(第%s章)%s" % (self.course, self.chapter, self.name)
############################# 课时表 ################################
class CourseSection(BaseModel):
"""
课时
跟章节是一(章节)对多(课时)关系,关系字段写在多的一方(课时)
一个章节可以有多个课时
"""
section_type_choices = (
(0, '文档'),
(1, '练习'),
(2, '视频')
)
name = models.CharField(max_length=128, verbose_name="课时标题")
orders = models.PositiveSmallIntegerField(verbose_name="课时排序")
section_type = models.SmallIntegerField(default=2, choices=section_type_choices, verbose_name="课时种类")
section_link = models.CharField(max_length=255, blank=True, null=True, verbose_name="课时链接",
help_text="若是video,填vid,若是文档,填link")
duration = models.CharField(verbose_name="视频时长", blank=True, null=True, max_length=32) # 仅在前端展示使用
pub_date = models.DateTimeField(verbose_name="发布时间", auto_now_add=True)
free_trail = models.BooleanField(verbose_name="是否可试看", default=False)
# !!!重点!!!: 一个章节可以有多个课时. 一个课时不包含多个章节. 关系: 课程和章节, 课时是多的一方 -> 一对多
chapter = models.ForeignKey("CourseChapter", related_name='coursesections', on_delete=models.CASCADE,
db_constraint=False, verbose_name="课程章节")
class Meta:
db_table = "luffy_course_Section"
verbose_name = "课时"
verbose_name_plural = verbose_name
def __str__(self):
return "%s-%s" % (self.chapter, self.name)
2. 注册models:course/adminx.py
import xadmin
from . import models
xadmin.site.register(models.CourseCategory)
xadmin.site.register(models.Course)
xadmin.site.register(models.Teacher)
xadmin.site.register(models.CourseChapter)
xadmin.site.register(models.CourseSection)
3. 数据库迁移
python manage.py makemigrations
python manage.py migrate
三、流程
1. 创建课程应用, 注册应用
2. 分析课程表, 构建课程表, 迁移课程表, 插入数据
3. 修改之前的BaseModel表中的update_time变成updated_time, create_time变成created_time, display_order变成orders
4. 课程引用下admin.py变成adminx.py, 并注册
5. 修改轮播图的试图接口, 将使用display_order排序的字段变成orders, 还有修改轮播图的定时任务