項目課程模塊


課程模塊

我們要開始寫課程模塊了~~課程模塊都有哪些功能呢~~

我們的課程模塊,包括了免費課程以及專題課程兩個方向~~

主要是課程的展示,點擊課程進入課程詳細頁面~~

課程詳細頁面展示,課程的概述,課程的價格策略,課程章節,評價以及常見問題~~

以及一些周邊的字段~~~這是主線路~~

根據功能設計表結構

對照着對標路飛網站,我們可以先討論下表結構~~

我們初步能夠確定下來12張表~~

from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType

# Create your models here.
__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="課程的分類")
    COURSE_TYPE_CHOICES = ((0, "付費"), (1, "vip專享"), (2, "學位課程"))
    course_type = models.SmallIntegerField(choices=COURSE_TYPE_CHOICES)
    degree_course = models.ForeignKey(to="DegreeCourse", blank=True, null=True, help_text="如果是學位課程,必須關聯學位表")

    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")
    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")
    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")
    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" % (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)  # 關聯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)  # 關聯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)
    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="會員名")
    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="用戶姓名")
    # head_img = models.CharField(max_length=256, default='/static/frontend/head_portrait/logo@2x.png',
    #                             verbose_name="個人頭像")

    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")
    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
        verbose_name_plural = verbose_name
        unique_together = ('course_detail', 'title')
課程模塊表結構

接口的編寫

我們表結構定下來以后,可以根據業務場景看需要哪些接口~~~

對於我們課程這個模塊,所有的功能都是展示,基於數據展示的,我們通常稱為數據接口~~

這種接口對於我們來說是最簡單的~~因為我們只是從數據庫拿數據~然后進行展示~~

我們來看下都需要哪些接口~~~

  -- 課程頁面  有課程所有分類這個接口

  -- 課程頁面  有展示課程的接口

  -- 點擊課程進入課程詳情頁面,詳情頁面的數據接口~~

  -- 詳情頁面下的子路由對應子組件的數據接口

    -- 課程章節課時

    -- 課程的評論

    -- 課程的常見問題

這些所有的數據接口~~套路都是讀取數據庫,序列化數據,返回~~~

那主要就是用DRF的序列化組件~~那我們從路由~然后序列化組件~然后看視圖~~

# by gaoxin
from django.conf.urls import url, include
from .views import CourseListView, CourseCategoryView, CourseDetailView, CourseChapterView
from .views import CourseCommentView, OftenAskedQuestionView

# 在項目路由下做了一層分發
# url(r'^api/course/', include("course.urls")),
urlpatterns = [
    url(r"^$", CourseListView.as_view()),
    url(r"^category$", CourseCategoryView.as_view()),
    url(r"^detail/(?P<pk>\d+)$", CourseDetailView.as_view()),
    url(r"^(?P<pk>\d+)/chapters$", CourseChapterView.as_view()),
    url(r"^(?P<pk>\d+)/comment$", CourseCommentView.as_view()),
    url(r"^(?P<pk>\d+)/often-asked-question$", OftenAskedQuestionView.as_view()),
]
urls.py
# by gaoxin
from rest_framework import serializers
from .models import Course, Category, CourseDetail, CourseChapter, Comment, OftenAskedQuestion


class CourseSerializer(serializers.ModelSerializer):
    level = serializers.CharField(source="get_level_display")
    price_policy = serializers.SerializerMethodField()

    def get_price_policy(self, obj):
        price_obj = obj.price_policy.all().order_by("price").first()
        return price_obj.price

    class Meta:
        model = Course
        fields = ["id", "title", "brief", "course_img", "study_num", "level", "price_policy"]


class CourseCategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = ["id", "title"]


class CourseDetailSerializer(serializers.ModelSerializer):
    level = serializers.CharField(source="course.get_level_display")
    study_num = serializers.IntegerField(source="course.study_num")
    price_policy = serializers.SerializerMethodField()
    course_outline = serializers.SerializerMethodField()
    recommend_courses = serializers.SerializerMethodField()
    teachers = serializers.SerializerMethodField()

    def get_teachers(self, obj):
        return [{"id":item.id, "name": item.name} for item in obj.teachers.all()]

    def get_recommend_courses(self, obj):
        return [{"id": item.id, "title": item.title} for item in obj.recommend_courses.all()]

    def get_course_outline(self, obj):
        outlines = obj.course_outline.all().order_by("order")
        return [{"title":outline.title, "content": outline.content} for outline in outlines]

    def get_price_policy(self, obj):
        return [{"id": price_obj.id, "valid_price_display": price_obj.get_valid_period_display(), "price": price_obj.price} for price_obj in obj.course.price_policy.all()]

    class Meta:
        model = CourseDetail
        fields = ["id", "summary", "hours", "level", "study_num", "price_policy", "why_study",
                  "what_to_study_brief", "course_outline", "career_improvement", "prerequisite",
                  "recommend_courses", "teachers"]


class CourseChapterSerializer(serializers.ModelSerializer):
    sections = serializers.SerializerMethodField()

    def get_sections(self, obj):
        sections = obj.course_sections.all().order_by("section_order")
        return [{"id": section.id, "title": section.title, "free_trail": section.free_trail} for section in sections]

    class Meta:
        model = CourseChapter
        fields = ["id", "title", "sections"]


class CourseCommentSerializer(serializers.ModelSerializer):
    account = serializers.CharField(source="account.username")

    class Meta:
        model = Comment
        fields = ["id", "account", "content", "date"]


class OftenAskQuestionSerializer(serializers.ModelSerializer):

    class Meta:
        model = OftenAskedQuestion
        fields = ["id", "question", "answer"]
serializers.py
class CourseCategoryView(APIView):
    """課程分類接口"""

    def get(self, request):
        queryset = Category.objects.all()
        ser_obj = CourseCategorySerializer(queryset, many=True)
        return Response(ser_obj.data)


class CourseListView(APIView):
    """查看所有免費課程的接口"""
    def get(self, request):
        category_id = request.query_params.get("category", 0)
        if category_id == 0:
            # 證明沒有分類,可以拿所有的課程數據
            queryset = Course.objects.all().order_by("order")
        else:
            queryset = Course.objects.filter(category_id=category_id).order_by("order")
        ser_obj = CourseSerializer(queryset, many=True)
        return Response(ser_obj.data)


class CourseDetailView(APIView):
    """課程詳情頁面"""
    def get(self, request, pk):
        course_detail_obj = CourseDetail.objects.filter(course__id=pk).first()
        if course_detail_obj:
            ser_obj = CourseDetailSerializer(course_detail_obj)
            return Response(ser_obj.data)
        else:
            return Response({"code": "1001", "msg": "查詢的課程不存在"})


class CourseChapterView(APIView):
    """課程章節接口"""
    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)


class CourseCommentView(APIView):
    def get(self, request, pk):
        queryset = Course.objects.filter(pk=pk).first().course_comments.all()
        ser_obj = CourseCommentSerializer(queryset, many=True)
        return Response(ser_obj.data)


class OftenAskedQuestionView(APIView):
    def get(self, request, pk):
        queryset = Course.objects.filter(pk=pk).first().often_ask_questions.all()
        ser_obj = OftenAskQuestionSerializer(queryset, many=True)
        return Response(ser_obj.data)
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)
升級版視圖的示例

我們可以根據上面的更改的視圖的示例~~來更改我們所有的視圖~~小伙伴們~自己動起手來吧~~

我們課程這個模塊下的數據接口我們就介紹這些~~

我們這里涉及課程圖片~~用我們Django的mediaURL~~

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


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^api/course/', include("course.urls")),

    # media路徑配置
    url(r'media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT})
]

這樣我們上傳的圖片~數據庫存的是路徑地址~~我們前端向后端的media路徑發送請求~~

拿到我們想要的圖片,視頻等資源~~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM