課程模塊,包括免費課程以及專題課程兩個,主要是課程的展現,點擊課程進入課程詳細頁面前端
爲了方便,每張表在數據庫中添加了中文名nginx
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') # 一個課程詳情只有一個大綱
不要忘了註冊到admin數據庫
from . import models for table in models.__all__: admin.site.register(getattr(models, table))
對於課程這個模塊,全部的功能都是展現,基於數據展現的,咱們一般稱爲數據接口django
這種接口對於咱們來講是最簡單的,從數據庫拿數據,而後進行展現後端
須要的接口api
-- 課程頁面 課程全部分類數據結構
展現課程介紹的接口frontend
-- 點擊課程進入課程詳情頁面,詳情頁面的數據接口ide
-- 詳情頁面下的子路由對應子組件的數據接口學習
課程章節課時
課程的評論
課程的常見問題
數據接口,都是讀取數據庫,序列化數據,返回
父路由分發 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)
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)
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()), ]
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"]
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)
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) 升級版視圖的示例
# 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的靜態資源文件夾下面