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

根据功能设计表结构

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

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.titleclass Meta:verbose_name = "01-课程分类表"db_table = verbose_nameverbose_name_plural = verbose_nameclass 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.titleclass Meta:verbose_name = "02-课程表"db_table = verbose_nameverbose_name_plural = verbose_nameclass 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.titleclass Meta:verbose_name = "03-课程详细表"db_table = verbose_nameverbose_name_plural = verbose_nameclass Teacher(models.Model):"""讲师表"""name = models.CharField(max_length=32, verbose_name="讲师名字")brief = models.TextField(max_length=1024, verbose_name="讲师介绍")def __str__(self):return self.nameclass Meta:verbose_name = "04-教师表"db_table = verbose_nameverbose_name_plural = verbose_nameclass DegreeCourse(models.Model):"""字段大体跟课程表相同,哪些不同根据业务逻辑去区分"""title = models.CharField(max_length=32, verbose_name="学位课程名字")def __str__(self):return self.titleclass Meta:verbose_name = "05-学位课程表"db_table = verbose_nameverbose_name_plural = verbose_nameclass 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.titleclass Meta:verbose_name = "06-课程章节表"db_table = verbose_nameverbose_name_plural = verbose_nameunique_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.chapterdef course_name(self):return self.chapter.course.titledef __str__(self):return "%s-%s-%s课时" % (self.chapter.course,self.chapter, self.title)class Meta:verbose_name = "07-课程课时表"db_table = verbose_nameverbose_name_plural = verbose_nameunique_together = ('chapter', 'section_link')class PricePolicy(models.Model):"""价格策略表"""content_type = models.ForeignKey(ContentType, on_delete=None)  # 关联course or degree_courseobject_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_nameverbose_name_plural = verbose_nameunique_together = ("content_type", 'object_id', "valid_period")class OftenAskedQuestion(models.Model):"""常见问题"""content_type = models.ForeignKey(ContentType, on_delete=None)  # 关联course or degree_courseobject_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_nameverbose_name_plural = verbose_nameunique_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.contentclass Meta:verbose_name = "10-评价表"db_table = verbose_nameverbose_name_plural = verbose_nameclass 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.usernameclass Meta:verbose_name = "11-用户表"db_table = verbose_nameverbose_name_plural = verbose_nameclass 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.titleclass Meta:verbose_name = "12-课程大纲表"  #db_table = verbose_name                         # 线上运行时不要写db_tableverbose_name_plural = verbose_nameunique_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 modelsclass CategorySerializer(serializers.ModelSerializer):class Meta:model = models.Categoryfields = "__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.Commentfields = ["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 PolyvViewurlpatterns = [# 课程分类path('category', CategoryView.as_view()),# 课程path('list', CourseView.as_view()),#               课程idpath('detail/<int:pk>', CourseDetailView.as_view()),# 课程的章节接口  课程idpath('chapter/<int:pk>', CourseChapterView.as_view()),#  评论         课程idpath('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 modelsclass CategorySerializer(serializers.ModelSerializer):class Meta:model = models.Categoryfields = "__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().priceclass Meta:model = models.Coursefields = ["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 inobj.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} forprice 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.CourseDetailfields = ["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 inobj.course_sections.all().order_by("section_order")]class Meta:model = models.CourseChapterfields = ["id", "title", "sections"]class CourseCommentSerializer(serializers.ModelSerializer):account = serializers.CharField(source="account.username")class Meta:model = models.Commentfields = ["id", "account", "content", "date"]class QuestionSerializer(serializers.ModelSerializer):class Meta:model = models.OftenAskedQuestionfields = ["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, QuestionSerializerclass 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):# 获取过滤条件中的分类IDcategory_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.pySTATIC_URL = '/static/'
# Media配置
MEDIA_URL = "media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")

# urls.pyfrom 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. etcd 笔记(05)— etcd 代码结构、各模块功能、整体架构、各模块之间的交互、请求和应答流程

    1. etcd 项目结构和功能 etcd 项目代码的目录结构如下: $ tree ├── auth ├── build ├── client ├── clientv3 ├── contrib ├── ...

  2. OpenCV 笔记(01)— OpenCV 概念、整体架构、各模块主要功能

    1. OpenCV 概念 图像处理( Image Processing )是用计算机对图像进行分析, 以达到所需结果的技术, 又称影像处理. 图像处理技术一般包括图像压缩, 增强和复原, 匹配.描述和 ...

  3. Python 多线程总结(1)- thread 模块

    thread 模块 1. 单线程 首先看下单线程程序运行的例子,如下所示, import timedef loop0():print 'start loop0 begin', time.ctime() ...

  4. 关于python导入模块和package的一些深度思考

    背景 在python中有导入模块和导入package一说,这篇文章主要介绍导入模块和package的一些思考. 首先什么是模块?什么是package? 模块:用来从逻辑上组织python代码(变量,函 ...

  5. Python Re 模块超全解读!详细

    内行必看!Python Re 模块超全解读! 2019.08.08 18:59:45字数 953阅读 121 re模块下的函数 compile(pattern):创建模式对象 > import ...

  6. python性能分析之line_profiler模块-耗时,效率 时间

    20210203 直接用pycharm 自带的 20201215 直接装不上的情况下 先下载安装文件 再安装 line_profiler使用装饰器(@profile)标记需要调试的函数.用kernpr ...

  7. python:Json模块dumps、loads、dump、load介绍

    20210831 https://www.cnblogs.com/bigtreei/p/10466518.html json dump dumps 区别 python:Json模块dumps.load ...

  8. 关于python 中的__future__模块

    Python的每个新版本都会增加一些新的功能,或者对原来的功能作一些改动.有些改动是不兼容旧版本的,也就是在当前版本运行正常的代码,到下一个版本运行就可能不正常了. 具体说来就是,某个版本中出现了某个 ...

  9. GPUtil是一个Python模块,使用nvidia-smi从NVIDA GPU获取GPU状态

    GPUtil是一个Python模块,使用nvidia-smi从NVIDA GPU获取GPU状态 一个Python模块,用于在Python中使用nvidia-smi以编程方式从NVIDA GPU获取GP ...

  10. Python多线程(3)——Queue模块

    Python多线程(3)--Queue模块 Queue模块支持先进先出(FIFO)队列,支持多线程的访问,包括一个主要的类型(Queue)和两个异常类(exception classes). Pyth ...

最新文章

  1. View的生命周期方法和Activity生命周期方法关系
  2. 【收藏】使用命令行创建maven web项目
  3. 十、Hadoop学习笔记————Hive与Hbase以及RDBMS(关系型数据库)的关系
  4. 程序人生:入门程序员最容易踩的 7 个坑!
  5. Android插件化开发之用DexClassLoader加载未安装的APK资源文件来实现app切换背景皮肤
  6. 蓝桥杯小白系列之汇编点亮led灯
  7. Ionic2 下处理 Android 设备下返回按钮的事件
  8. 2014计算机一级选择题,计算机一级选择题题库2014含答案
  9. 最新关于高德地图定位失败10:定位服务启动、解决办法
  10. matlab三维货位图,基于matlab立体仓库静态货位分配优化及仿真
  11. 圣思园java笔记_最详细JAVA高并发多线程VIP课程--圣思园--笔记
  12. 一键Pad变电脑,安全快速的远控办公这不就来了。
  13. 计算机网络名称缓存清理,如何清理电脑网络连接缓存
  14. 小虾米闯江湖服务器维护中,小虾米闯江湖数据总结及中期注意事项一览
  15. python工作遇到的问题_工作中遇到的问题收集--.NET
  16. Windows系统下编译torch-points-kernels
  17. mba和研究生的区别
  18. python——常用的数学计算公式
  19. 调用sleep后,我做了一个噩梦
  20. 打开Word文档的时候提示mathtype “安全警告 宏已被禁用”

热门文章

  1. 用计算机弹没那种命,成熟小女人QQ个性网名 没那种命就别犯那种病
  2. java栈存储_【转载】Java中的数据存储(堆、栈、常量池)
  3. python中emuterate用法_跨平台模拟执行 - AndroidNativeEmu实用手册
  4. linux配置python环境变量_linux添加PYTHONPATH环境变量
  5. python代码_如何使用 Sphinx 给 Python 代码写文档
  6. 狂神java什么来头_狂神说SpringBoot18:集成SpringSecurity
  7. php 正则表达式 环视,php正则表达式环视详解
  8. 和cnn结合_写给小白的R-CNN介绍
  9. zeppelin安装使用
  10. 学了半天,import 到底在干啥?