項目課程模塊

  課程模塊,包括了免費課程以及專題課程兩個方向。前端

  主要是課程的展現,點擊課程進入課程詳細頁面。課程詳細頁面展現,課程的概述,課程的價格策略,課程章節,評價以及常見問題。數據庫

1、根據功能設計表結構

一、設計表結構

  在項目中建立課程模塊APP——Course。設計表結構以下所示:django

from django.db import models
# Create your models here.

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="課程的分類", on_delete=None)   # 課程分類和課程是一對多的關係

    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="若是是學位課程,必須關聯學位表", 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")
    # GenericRelation只用於反向查詢不生成字段
    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)   # 執行父類的save方法

    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")   # 聯合惟一,課程下的章節應該是惟一的(好比:第2章第2節)


class CourseSection(models.Model):
    """課時表"""
    chapter = models.ForeignKey(to="CourseChapter", related_name="course_sections", on_delete=None) # 章節與課時:一對多關係
    title = models.CharField(max_length=32, verbose_name="課時")   # 課時名稱:好比:認證組件介紹
    section_order = models.SmallIntegerField(verbose_name="課時排序", help_text="建議每一個課時之間空1至2個值,以備後續插入課時")
    free_trail = models.BooleanField("是否可試看", default=False)   # 是否可試看
    section_type_choices = ((0, '文檔'), (1, '練習'), (2, '視頻'))   # 對應不一樣的連接
    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):
    """價格策略表,ContentType來實現價格策略,避免出現多個ForeignKey"""
    content_type = models.ForeignKey(ContentType, on_delete=None)  # 價格策略與contentType表創建外鍵關係,同時關聯course表和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()      # 價格,float格式

    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="我的頭像")

    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)   # TextField能夠保存並展現空格、回車

    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')   # 聯合惟一:一個課程詳情下的大綱標題只能有一個

  LuffyCity/Course/models.py文件初始內容設計如上所示。後端

二、添加表數據

  在完成數據庫遷移和管理員建立後。經過admin組件在數據庫添加數據示例以下所示:api

  

 

2、接口編寫——課程分類接口

  表結構基本定下來後,能夠根據業務場景看須要哪些接口。frontend

  對於課程這個模塊,全部的功能都是展現,基於數據展現,一般稱爲數據接口ide

一、梳理須要哪些接口

  課程頁面:課程全部分類的接口、展現課程的接口。post

  課程詳情頁面:點擊課程要進入課程詳情頁面,即詳情頁面的數據接口。學習

  詳情頁面下子路由對應子組件數據接口:課程章節課時、課程評論、課程常見問題。ui

  能夠看到,上述接口都是讀取數據庫、序列化數據、返回。所以主要是使用DRF的序列化組件實現。

二、註冊數據模型到 admin

  編寫LuffyCity/Course/admin.py文件以下所示:

from django.contrib import admin

# Register your models here.
from . import models

for table in models.__all__:      # __all__變量保存全部的表名
    admin.site.register(getattr(models, table))      # 用反射在models中找到每個表並註冊進來

  因爲models定義了__all__屬性,以列表格式保存全部表名。

  所以可使用上述方法將Model中全部類註冊,便可在Admin中實現增刪改查的功能。

三、課程路由配置

  編寫LuffyCity/Course/urls.py文件以下所示:

from django.urls import path
from .views import CategoryView


urlpatterns = [
    path('category', CategoryView.as_view()),
]

四、序列化數據

  建立新文件:LuffyCity/Course/serializers.py,在該文件中執行序列化操做。

from rest_framework import serializers
from . import models


class CategorySerializer(serializers.ModelSerializer):
    class Meta:   # 配置元信息
        model = models.Category    # 課程分類表
        fields = "__all__"

五、課程視圖編寫

  編寫課程分類視圖以下所示:

from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from . import models    # 引入全部的models
from .serializers import CategorySerializer    # 引入序列化器
# Create your views here.


class CategoryView(APIView):
    def get(self, request):
        # 經過ORM操做獲取全部分類數據
        queryset = models.Category.objects.all()
        # 利用DRF序列化器去序列化數據
        ser_obj = CategorySerializer(queryset, many=True)
        # 返回
        return Response(ser_obj.data)

  執行程序後,經過postman訪問接口以下所示:

  

3、獲取課程接口

一、添加課程url

  在LuffyCity/Course/urls.py中添加查看課程內容:

from django.urls import path
from .views import CategoryView, CourseView


urlpatterns = [
    path('category', CategoryView.as_view()),   # 課程分類
    path('list', CourseView.as_view())    # 查看課程
]

二、添加課程序列號

  在LuffyCity/Course/serializers.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字段獲取的是數字,須要處理
    level = serializers.CharField(source="get_level_display")   # 指定資源,執行ORM操做get_level_display,拿到中文展現
    # 根據價格策略獲取價格數據
    price = serializers.SerializerMethodField()

    def get_price(self, obj):
        # 拿到全部的價格策略中,最便宜的價格
        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"]   # 提取部分字段

三、添加課程視圖

  在LuffyCity/Course/views.py中添加課程視圖內容:

from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from . import models    # 引入全部的models
from .serializers import CategorySerializer, CourseSerializer   # 引入序列化器
# Create your views here.


class CategoryView(APIView):...


class CourseView(APIView):
    def get(self, request):
        # 獲取過濾條件中的分類id
        category_id = request.query_params.get("category", 0)   # 默認id爲0,即全部課程
        # 分類id做爲過濾條件來獲取課程
        if category_id == 0:
            queryset = models.Course.objects.all().order_by("order")    # 以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)

4、Django的MEDIA配置

  靜態資源(多媒體資源)配置。

一、settings.py配置

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/

STATIC_URL = '/static/'

# Media配置
MEDIA_URL = "media/"        # 前端訪問圖片資源,存的時候要放一個路徑
MEDIA_ROOT = os.path.join(BASE_DIR, "media")

(1)MEDIA_ROOT

  默認值: '' (空的字符串)

  一個絕對路徑, 用於保存媒體文件. 例子: "/home/media/media.lawrence.com/" 。

(2)MEDIA_URL

  默認值: '' (空的字符串)

  處理媒體服務的URL(媒體文件來自 MEDIA_ROOT). 如: "http://media.lawrence.com"。

二、urls.py配置media路徑配置

  修改LuffyCity/LuffyCity/urls.py以下所示:

from django.contrib import admin
from django.urls import path, include, re_path
from django.views.static import serve
from LuffyCity import settings


urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/course/', include("Course.urls")),

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

  經過admin組件添加數據,爲課程添加圖片數據。能夠看到項目自動建立media目錄以下所示:

  

 

三、訪問接口查看保存的圖片

  訪問接口以下所示:

  

 

  所以咱們上傳的圖片,數據庫保存的是路徑地址,前端向後端的media路徑發送請求來拿到圖片、視頻等資源。

相關文章
相關標籤/搜索