zoukankan      html  css  js  c++  java
  • qhfl-3 Course模块

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

    根据功能设计表结构

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

    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)

    image

    课程章节接口

    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)
    

    image

    评论以及常见问题接口

    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)

    image

    image

    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的静态资源文件夹下面

  • 相关阅读:
    如何在VIM中编辑并保存退出!!!
    MapReduce---数据清洗
    构建之法阅读笔记03
    人月神话阅读笔记03
    学习进度报告16
    大道至简阅读笔记03
    大道至简阅读笔记02
    个人总结
    大道至简阅读笔记01
    学习进度报告15
  • 原文地址:https://www.cnblogs.com/wenyule/p/10467637.html
Copyright © 2011-2022 走看看