课程模块,包括免费课程以及专题课程两个,主要是课程的展示,点击课程进入课程详细页面

根据功能设计表结构

为了方便,每张表在数据库中添加了中文名

from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
# from shopping.models import OrderDetail,Coupon # 注册admin 的时候 方便引入
__all__ = ["Category", "Course", "CourseDetail", "Teacher", "DegreeCourse", "CourseChapter",
"CourseSection", "PricePolicy", "OftenAskedQuestion", "Comment", "Account", "CourseOutline"] class Category(models.Model):
"""课程分类表"""
title = models.CharField(max_length=32, unique=True, verbose_name="课程的分类") def __str__(self):
return self.title class Meta:
verbose_name = "01-课程分类表"
db_table = verbose_name
verbose_name_plural = verbose_name class Course(models.Model):
"""课程表"""
title = models.CharField(max_length=128, unique=True, verbose_name="课程的名称")
course_img = models.ImageField(upload_to="course/%Y-%m", verbose_name='课程的图片')
category = models.ForeignKey(to="Category", verbose_name="课程的分类", on_delete=None) COURSE_TYPE_CHOICES = ((0, "付费"), (1, "vip专享"), (2, "学位课程"),(3,"免费"))
course_type = models.SmallIntegerField(choices=COURSE_TYPE_CHOICES)
degree_course = models.ForeignKey(to="DegreeCourse", blank=True, null=True, help_text="如果是学位课程,必须关联学位表",
on_delete=None) brief = models.CharField(verbose_name="课程简介", max_length=1024)
level_choices = ((0, '初级'), (1, '中级'), (2, '高级'))
level = models.SmallIntegerField(choices=level_choices, default=1) status_choices = ((0, '上线'), (1, '下线'), (2, '预上线'))
status = models.SmallIntegerField(choices=status_choices, default=0)
pub_date = models.DateField(verbose_name="发布日期", blank=True, null=True) order = models.IntegerField("课程顺序", help_text="从上一个课程数字往后排")
study_num = models.IntegerField(verbose_name="学习人数", help_text="只要有人买课程,订单表加入数据的同时给这个字段+1") # order_details = GenericRelation("OrderDetail", related_query_name="course")
# coupon = GenericRelation("Coupon")
# 只用于反向查询不生成字段
price_policy = GenericRelation("PricePolicy")
often_ask_questions = GenericRelation("OftenAskedQuestion")
course_comments = GenericRelation("Comment") def save(self, *args, **kwargs):
if self.course_type == 2:
if not self.degree_course:
raise ValueError("学位课必须关联学位课程表")
super(Course, self).save(*args, **kwargs) def __str__(self):
return self.title class Meta:
verbose_name = "02-课程表"
db_table = verbose_name
verbose_name_plural = verbose_name class CourseDetail(models.Model):
"""课程详细表"""
course = models.OneToOneField(to="Course", on_delete=None)
hours = models.IntegerField(verbose_name="课时", default=7)
course_slogan = models.CharField(max_length=125, blank=True, null=True, verbose_name="课程口号")
video_brief_link = models.CharField(max_length=255, blank=True, null=True)
summary = models.TextField(max_length=2048, verbose_name="课程概述")
why_study = models.TextField(verbose_name="为什么学习这门课程")
what_to_study_brief = models.TextField(verbose_name="我将学到哪些内容")
career_improvement = models.TextField(verbose_name="此项目如何有助于我的职业生涯")
prerequisite = models.TextField(verbose_name="课程先修要求", max_length=1024)
recommend_courses = models.ManyToManyField("Course", related_name="recommend_by", blank=True) teachers = models.ManyToManyField("Teacher", verbose_name="课程讲师") def __str__(self):
return self.course.title class Meta:
verbose_name = "03-课程详细表"
db_table = verbose_name
verbose_name_plural = verbose_name class Teacher(models.Model):
"""讲师表"""
name = models.CharField(max_length=32, verbose_name="讲师名字")
brief = models.TextField(max_length=1024, verbose_name="讲师介绍") def __str__(self):
return self.name class Meta:
verbose_name = "04-教师表"
db_table = verbose_name
verbose_name_plural = verbose_name class DegreeCourse(models.Model):
"""
字段大体跟课程表相同,哪些不同根据业务逻辑去区分
"""
title = models.CharField(max_length=32, verbose_name="学位课程名字") def __str__(self):
return self.title class Meta:
verbose_name = "05-学位课程表"
db_table = verbose_name
verbose_name_plural = verbose_name class CourseChapter(models.Model):
"""课程章节表"""
course = models.ForeignKey(to="Course", related_name="course_chapters", on_delete=None)
chapter = models.SmallIntegerField(default=1, verbose_name="第几章")
title = models.CharField(max_length=32, verbose_name="课程章节名称") def __str__(self):
return self.title class Meta:
verbose_name = "06-课程章节表"
db_table = verbose_name
verbose_name_plural = verbose_name
unique_together = ("course", "chapter") class CourseSection(models.Model):
"""课时表""" chapter = models.ForeignKey(to="CourseChapter", related_name="course_sections",verbose_name="self.chapter.course.title", on_delete=None)
title = models.CharField(max_length=32, verbose_name="课时")
section_order = models.SmallIntegerField(verbose_name="课时排序", help_text="建议每个课时之间空1至2个值,以备后续插入课时")
section_type_choices = ((0, '文档'), (1, '练习'), (2, '视频'))
free_trail = models.BooleanField("是否可试看", default=False)
section_type = models.SmallIntegerField(default=2, choices=section_type_choices)
section_link = models.CharField(max_length=255, blank=True, null=True, help_text="若是video,填vid,若是文档,填link") def course_chapter(self):
return self.chapter.chapter def course_name(self):
return self.chapter.course.title def __str__(self):
return "%s-%s-%s课时" % (self.chapter.course,self.chapter, self.title) class Meta:
verbose_name = "07-课程课时表"
db_table = verbose_name
verbose_name_plural = verbose_name
unique_together = ('chapter', 'section_link') class PricePolicy(models.Model):
"""价格策略表"""
content_type = models.ForeignKey(ContentType, on_delete=None) # 关联course or degree_course
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id') valid_period_choices = ((1, '1天'), (3, '3天'),
(7, '1周'), (14, '2周'),
(30, '1个月'),
(60, '2个月'),
(90, '3个月'),
(120, '4个月'),
(180, '6个月'), (210, '12个月'),
(540, '18个月'), (720, '24个月'),
(722, '24个月'), (723, '24个月'),
)
valid_period = models.SmallIntegerField(choices=valid_period_choices)
price = models.FloatField() def __str__(self):
return "%s(%s)%s" % (self.content_object, self.get_valid_period_display(), self.price) class Meta:
verbose_name = "08-价格策略表"
db_table = verbose_name
verbose_name_plural = verbose_name
unique_together = ("content_type", 'object_id', "valid_period") class OftenAskedQuestion(models.Model):
"""常见问题"""
content_type = models.ForeignKey(ContentType, on_delete=None) # 关联course or degree_course
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id') question = models.CharField(max_length=255)
answer = models.TextField(max_length=1024) def __str__(self):
return "%s-%s" % (self.content_object, self.question) class Meta:
verbose_name = "09-常见问题表"
db_table = verbose_name
verbose_name_plural = verbose_name
unique_together = ('content_type', 'object_id', 'question') class Comment(models.Model):
"""通用的评论表"""
content_type = models.ForeignKey(ContentType, blank=True, null=True, on_delete=None)
object_id = models.PositiveIntegerField(blank=True, null=True)
content_object = GenericForeignKey('content_type', 'object_id') content = models.TextField(max_length=1024, verbose_name="评论内容")
account = models.ForeignKey("Account", verbose_name="会员名", on_delete=None)
date = models.DateTimeField(auto_now_add=True) def __str__(self):
return self.content class Meta:
verbose_name = "10-评价表"
db_table = verbose_name
verbose_name_plural = verbose_name class Account(models.Model):
username = models.CharField(max_length=32, verbose_name="用户姓名")
pwd = models.CharField(max_length=32, verbose_name="密文密码")
# head_img = models.CharField(max_length=256, default='/static/frontend/head_portrait/logo@2x.png',
# verbose_name="个人头像")
balance = models.IntegerField(verbose_name="贝里余额", default=0) def __str__(self):
return self.username class Meta:
verbose_name = "11-用户表"
db_table = verbose_name
verbose_name_plural = verbose_name class CourseOutline(models.Model):
"""
课程大纲
跟课程详情外键关联,而不是直接跟课程,提高查询
"""
course_detail = models.ForeignKey(to="CourseDetail", related_name="course_outline", on_delete=None)
title = models.CharField(max_length=128)
order = models.PositiveSmallIntegerField(default=1)
# 前端显示顺序 content = models.TextField("内容", max_length=2048) def __str__(self):
return "%s" % self.title class Meta:
verbose_name = "12-课程大纲表" #
db_table = verbose_name # 线上运行时不要写db_table
verbose_name_plural = verbose_name
unique_together = ('course_detail', 'title') # 一个课程详情只有一个大纲

Course/models.py

不要忘了注册到admin

from . import models
for table in models.__all__:
admin.site.register(getattr(models, table))

接口

对于课程这个模块,所有的功能都是展示,基于数据展示的,我们通常称为数据接口

这种接口对于我们来说是最简单的,从数据库拿数据,然后进行展示

需要的接口

  -- 课程页面  课程所有分类

        展示课程介绍的接口

  -- 点击课程进入课程详情页面,详情页面的数据接口

  -- 详情页面下的子路由对应子组件的数据接口

     课程章节课时

     课程的评论

     课程的常见问题

数据接口,都是读取数据库,序列化数据,返回

父路由分发
path('api/course/', include("Course.urls")),

课程分类接口

from rest_framework import serializers
from . import models class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = models.Category
fields = "__all__"
class CategoryView(APIView):
def get(self, request):
# 通过ORM操作获取所有分类数据
queryset = models.Category.objects.all()
# 利用序列化器去序列化我们的数据
ser_obj = CategorySerializer(queryset, many=True)
# 返回
return Response(ser_obj.data)

课程详情接口

CourseDetailSerializer
path('detail/<int:pk>', CourseDetailView.as_view()),

class CourseDetailView(APIView):
def get(self, request, pk):
# 根据pk获取到课程详情对象
course_detail_obj = models.CourseDetail.objects.filter(course__id=pk).first()
if not course_detail_obj:
return Response({"code": 1001, "error": "查询的课程详情不存在"})
# 序列化课程详情
ser_obj = CourseDetailSerializer(course_detail_obj)
# 返回
return Response(ser_obj.data)

课程章节接口

CourseChapterSerializer
class CourseChapterView(APIView):
def get(self, request, pk):
# ["第一章": {课时一, 课时二}]
queryset = models.CourseChapter.objects.filter(course_id=pk).all().order_by("chapter")
# 序列化章节对象
ser_obj = CourseChapterSerializer(queryset, many=True)
# 返回
return Response(ser_obj.data)

评论以及常见问题接口

class CourseCommentSerializer(serializers.ModelSerializer):
account = serializers.CharField(source="account.username") class Meta:
model = models.Comment
fields = ["id", "account", "content", "date"] class QuestionSerializer(serializers.ModelSerializer):
class Meta:
model = models.OftenAskedQuestion
fields = ["id", "question", "answer"]
class CourseCommentView(APIView):
def get(self, request, pk):
# 通过课程id找到课程所有的评论
queryset = models.Course.objects.filter(id=pk).first().course_comments.all()
# 序列化
ser_obj = CourseCommentSerializer(queryset, many=True)
# 返回
return Response(ser_obj.data) class QuestionView(APIView):
def get(self, request, pk):
queryset = models.Course.objects.filter(id=pk).first().often_ask_questions.all()
ser_obj = QuestionSerializer(queryset, many=True)
return Response(ser_obj.data)

from django.urls import path
from .views import CategoryView, CourseView, CourseDetailView, CourseChapterView, CourseCommentView, QuestionView
from .video_view import PolyvView urlpatterns = [
# 课程分类
path('category', CategoryView.as_view()), # 课程
path('list', CourseView.as_view()), # 课程id
path('detail/<int:pk>', CourseDetailView.as_view()), # 课程的章节接口 课程id
path('chapter/<int:pk>', CourseChapterView.as_view()), # 评论 课程id
path('comment/<int:pk>', CourseCommentView.as_view()), path('question/<int:pk>', QuestionView.as_view()), path('polyv', PolyvView.as_view()), ]

Course/urls.py

from rest_framework import serializers
from . import models class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = models.Category
fields = "__all__" class CourseSerializer(serializers.ModelSerializer):
level = serializers.CharField(source="get_level_display") # 直接将选项的数字,转换成了可显示的字符
price = serializers.SerializerMethodField() def get_price(self, obj):
print(obj.price_policy.all()) return obj.price_policy.all().order_by("price").first().price class Meta:
model = models.Course
fields = ["id", "title", "course_img", "brief", "level", "study_num", "price"] class CourseDetailSerializer(serializers.ModelSerializer):
level = serializers.CharField(source="course.get_level_display") # level 是选项时 get_xx_display 直接拿到选项的值
study_num = serializers.IntegerField(source="course.study_num")
recommend_courses = serializers.SerializerMethodField() # 涉及到跨表,用
teachers = serializers.SerializerMethodField()
price_policy = serializers.SerializerMethodField()
course_outline = serializers.SerializerMethodField() def get_course_outline(self, obj): # obj是课程对象
return [{"id": outline.id, "title": outline.title, "content": outline.content} for outline in
obj.course_outline.all().order_by("order")] def get_price_policy(self, obj):
return [{"id": price.id, "valid_price_display": price.get_valid_period_display(), "price": price.price} for
price in obj.course.price_policy.all()] def get_teachers(self, obj):
return [{"id": teacher.id, "name": teacher.name} for teacher in obj.teachers.all()] def get_recommend_courses(self, obj):
return [{"id": course.id, "title": course.title} for course in obj.recommend_courses.all()] class Meta:
model = models.CourseDetail
fields = ["id", "hours", "summary", "level", "study_num", "recommend_courses", "teachers",
"price_policy", "course_outline"] class CourseChapterSerializer(serializers.ModelSerializer):
sections = serializers.SerializerMethodField() def get_sections(self, obj):
return [{"id": section.id, "title": section.title, "free_trail": section.free_trail} for section in
obj.course_sections.all().order_by("section_order")] class Meta:
model = models.CourseChapter
fields = ["id", "title", "sections"] class CourseCommentSerializer(serializers.ModelSerializer):
account = serializers.CharField(source="account.username") class Meta:
model = models.Comment
fields = ["id", "account", "content", "date"] class QuestionSerializer(serializers.ModelSerializer):
class Meta:
model = models.OftenAskedQuestion
fields = ["id", "question", "answer"]

Course/serializers.py

from rest_framework.views import APIView
from rest_framework.response import Response
from . import models
from .serializers import CategorySerializer, CourseSerializer, CourseDetailSerializer, CourseChapterSerializer
from .serializers import CourseCommentSerializer, QuestionSerializer class CategoryView(APIView):
def get(self, request):
# 通过ORM操作获取所有分类数据
queryset = models.Category.objects.all()
# 利用序列化器去序列化我们的数据
ser_obj = CategorySerializer(queryset, many=True)
# 返回
return Response(ser_obj.data) class CourseView(APIView):
def get(self, request):
# 获取过滤条件中的分类ID
category_id = request.query_params.get("category", 0)
# 根据分类获取课程
if category_id == 0:
queryset = models.Course.objects.all().order_by("order")
else:
queryset = models.Course.objects.filter(category_id=category_id).all().order_by("order")
# 序列化课程数据
ser_obj = CourseSerializer(queryset, many=True)
# 返回
return Response(ser_obj.data) class CourseDetailView(APIView):
def get(self, request, pk):
# 根据pk获取到课程详情对象
course_detail_obj = models.CourseDetail.objects.filter(course__id=pk).first()
if not course_detail_obj:
return Response({"code": 1001, "error": "查询的课程详情不存在"})
# 序列化课程详情
ser_obj = CourseDetailSerializer(course_detail_obj)
# 返回
return Response(ser_obj.data) class CourseChapterView(APIView):
def get(self, request, pk):
# ["第一章": {课时一, 课时二}]
queryset = models.CourseChapter.objects.filter(course_id=pk).all().order_by("chapter")
# 序列化章节对象
ser_obj = CourseChapterSerializer(queryset, many=True)
# 返回
return Response(ser_obj.data) class CourseCommentView(APIView):
def get(self, request, pk):
# 通过课程id找到课程所有的评论
queryset = models.Course.objects.filter(id=pk).first().course_comments.all()
# 序列化
ser_obj = CourseCommentSerializer(queryset, many=True)
# 返回
return Response(ser_obj.data) class QuestionView(APIView):
def get(self, request, pk):
queryset = models.Course.objects.filter(id=pk).first().often_ask_questions.all()
ser_obj = QuestionSerializer(queryset, many=True)
return Response(ser_obj.data)

Course/views.py

class CourseCategoryView(generics.ListAPIView):
queryset = Category.objects.all()
serializer_class = CourseCategorySerializer
"""课程分类接口""" # def get(self, request):
# queryset = Category.objects.all()
# ser_obj = CourseCategorySerializer(queryset, many=True)
# return Response(ser_obj.data) class CourseChapterView(generics.RetrieveAPIView):
queryset = CourseChapter.objects.all()
serializer_class = CourseChapterSerializer
# 指定过滤的类 用排序的过滤类
filter_backends = (filters.OrderingFilter,)
# 排序的字段
ordering = ("chapter",) # def get(self, request, pk):
# # 首先我们要清楚数据结构
# # 我们要的是[章节一:{课时,课时2}]
# queryset = CourseChapter.objects.filter(course_id=pk).order_by("chapter")
# ser_obj = CourseChapterSerializer(queryset, many=True)
# return Response(ser_obj.data) 升级版视图的示例

Course/views.py 优化

Django的MEDIA配置

# settings.py

STATIC_URL = '/static/'
# Media配置
MEDIA_URL = "media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
# urls.py

from django.conf.urls import url, include
from django.contrib import admin
from django.views.static import serve
from new_luffy import settings
# media路径配置
re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT})

上传的图片,数据库存的是路径地址,前端向后端的media路径发送请求。

线上环境时可以将 MEDIA_ROOT 存放到nginx的静态资源文件夹下面

qhfl-3 Course模块的更多相关文章

  1. npm 私有模块的管理使用

    你可以使用 NPM 命令行工具来管理你在 NPM 仓库的私有模块代码,这使得在项目中使用公共模块变的更加方便. 开始前的工作 你需要一个 2.7.0 以上版本的 npm ,并且需要有一个可以登陆 np ...

  2. node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理

    一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...

  3. ES6模块import细节

    写在前面,目前浏览器对ES6的import支持还不是很好,需要用bable转译. ES6引入外部模块分两种情况: 1.导入外部的变量或函数等: import {firstName, lastName, ...

  4. Python标准模块--ContextManager

    1 模块简介 在数年前,Python 2.5 加入了一个非常特殊的关键字,就是with.with语句允许开发者创建上下文管理器.什么是上下文管理器?上下文管理器就是允许你可以自动地开始和结束一些事情. ...

  5. Python标准模块--Unicode

    1 模块简介 Python 3中最大的变化之一就是删除了Unicode类型.在Python 2中,有str类型和unicode类型,例如, Python 2.7.6 (default, Oct 26 ...

  6. Python标准模块--Iterators和Generators

    1 模块简介 当你开始使用Python编程时,你或许已经使用了iterators(迭代器)和generators(生成器),你当时可能并没有意识到.在本篇博文中,我们将会学习迭代器和生成器是什么.当然 ...

  7. 自己实现一个javascript事件模块

    nodejs中的事件模块 nodejs中有一个events模块,用来给别的函数对象提供绑定事件.触发事件的能力.这个别的函数的对象,我把它叫做事件宿主对象(非权威叫法),其原理是把宿主函数的原型链指向 ...

  8. 理解nodejs模块的scope

    描述 原文档地址:https://docs.npmjs.com/misc/scope 所有npm模块都有name,有的模块的name还有scope.scope的命名规则和name差不多,同样不能有ur ...

  9. nodejs模块发布及命令行程序开发

    前置技能 npm工具为nodejs提供了一个模块和管理程序模块依赖的机制,当我们希望把模块贡献出去给他人使用时,可以把我们的程序发布到npm提供的公共仓库中,为了方便模块的管理,npm规定要使用一个叫 ...

  10. 开始学nodejs——net模块

    net模块的组成部分 详见 http://nodejs.cn/api/net.html 下面整理出了整个net模块的知识结构,和各个事件.方法.属性的用法 net.Server类 net.Socket ...

随机推荐

  1. 学习 MeteoInfo二次开发教程(一)

    来自气象家园:http://bbs.06climate.com/forum.php?mod=viewthread&tid=6631 按照教程,没有太大问题,有些是对c#操作不熟悉导致. 1.添 ...

  2. (dev mode) install CONSUL on ubuntu

    WSL: V18.04.1 1. install $sudo apt-get update$sudo apt-get install consul wsl1017@DESKTOP-14G6K9S:~$ ...

  3. Servlet 教程 各个知识点简单概括

    Servlet 教程 http://www.ziqiangxuetang.com/servlet/servlet-cookies-handling.html Servlet 教程 Servlet 教程 ...

  4. 浅谈MyBatisGenerator的使用

    目录 1.概述 2.依赖 3.Maven插件配置 4.配置文件说明 5.运行 6.总结 1.概述 日常中使用MyBatis最为麻烦的就是编写Mapper文件了, 如果数据库增加一张表, 这时通常会复制 ...

  5. Python爬虫中文小说网点查找小说并且保存到txt(含中文乱码处理方法)

    从某些网站看小说的时候经常出现垃圾广告,一气之下写个爬虫,把小说链接抓取下来保存到txt,用requests_html全部搞定,代码简单,容易上手. 中间遇到最大的问题就是编码问题,第一抓取下来的小说 ...

  6. Windows 10同步时间的方法

    今天在安装了Windows 10 1809(October 2018 update)之后发现时间不能同步,以前并没有出现这种情况. 1) 打开控制面板,找到时钟域地区 2) 选择日期和时间 3) 选择 ...

  7. Oracle一次Insert多条数据

    insert all into JK_TB_DATE (fbmmc,fgzjh,fsbmc,fsbxh,fsbbh,db_shuifenyi,db_pihao,db_wuliaobianma) ',' ...

  8. 深入浅出PF 学习笔记---通过资源文件设置按钮的Style及通过x:null设置不受Style影响

    <Window x:Class="WpfStudy2018.StyleButton" xmlns="http://schemas.microsoft.com/win ...

  9. python 内置元祖子类

    a = (zhangsan,20,nv,123@163.com) 输出姓名 a[0] 输出年龄 a[1] 输出性别 a[2] ..... 这样写可读性非常低 使用 内置元祖子类 from collec ...

  10. springboot整合devtool无法热部署

    参见https://www.cnblogs.com/winner-0715/p/6666579.html.