课程模块
我们要开始写课程模块了~~课程模块都有哪些功能呢~~
我们的课程模块,包括了免费课程以及专题课程两个方向~~
主要是课程的展示,点击课程进入课程详细页面~~
课程详细页面展示,课程的概述,课程的价格策略,课程章节,评价以及常见问题~~
以及一些周边的字段~~~这是主线路~~
根据功能设计表结构
对照着对标路飞网站,我们可以先讨论下表结构~~
我们初步能够确定下来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路径发送请求~~
拿到我们想要的图片,视频等资源~~