python 全栈开发,Day99(作业讲解,DRF版本,DRF分页,DRF序列化进阶)
昨日内容回顾
1. 为什么要做前后端分离? - 前后端交给不同的人来编写,职责划分明确。 - API (IOS,安卓,PC,微信小程序...) - vue.js等框架编写前端时,会比之前写jQuery更简单快捷。 2. 简述http协议? - 基于socket - 数据格式: "GET /index?name=123&age=19 http1.1\r\nhost:www.luffyciti.com\r\ncontent-type:application/json...\r\n\r\n" "POST /index http1.1\r\nhost:www.luffyciti.com\r\ncontent-type:application/json...\r\n\r\n{name:'alex',age:18}" "POST /index http1.1\r\nhost:www.luffyciti.com\r\ncontent-type:application/enform.....\r\n\r\nname=alex&age=18&xx=19" - 无状态短链接 一次请求一次响应之后断开连接 3. 简述restful 规范? https://www.luffycity.com/api/v1/courses/?sub_category=0 https://www.luffycity.com/api/v1/courses/?sub_category=0 看上面一段url,可以说出5个 1. 使用https代替http 2.在URL中体现自己写的是API 3. 在URL中体现版本 4. 使用名词 5.参数要合理 之后,请求方式,响应信息。可以说后面5个 6. 根据请求方式不同,处理不同的操作 7.返回状态码 8.返回错误信息 9. 返回code 10.hyper link(超链接) 4. django rest framework组件的作用? - 快速实现符合restful 规范的api 5. 列举django rest framework组件(10个)? - 版本 - 权限 - 认证 - 节流 - 分页 - 解析器 - 序列化 - 视图 - 路由 - 渲染器 6. 路飞的表结构 a. 课程分类 - 课程大类 - 课程子类 b. 学位课 - 学位课 - 奖学金 - 老师 c. 专题课 or 学位课模块 - 专题课 or 学位课模块 - 课程详细 - 课程大纲 - 常见问题 - 章节 - 课时 - 作业 d. 价格 - 价格策略 7. django contenttypes组件的作用? 为了解决一张表和多个表做外键关联
上面都是面试题
列举django rest framework的10个组件,可以根据django请求生命周期来记
1. 请求路径包含版本,请求先到达路由,2.进入组件。认证,权限,节流。3.到达视图,获取参数,使用解析器。4.获取数据库记录,做序列化返回。
5.数据太多,要做分页,返回给用户之后,要做渲染,利用渲染器。
一、作业讲解
下面代码:
https://github.com/987334176/luffycity/archive/v1.zip
修改models.py
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.contrib.contenttypes.models import ContentType from django.db.models import Q from django.utils.safestring import mark_safe from django.db import models import hashlib # ######################## 课程相关 ######################## class CourseCategory(models.Model): """课程大类, e.g 前端 后端...""" name = models.CharField(max_length=64, unique=True) def __str__(self): return "%s" % self.name class Meta: verbose_name_plural = "01.课程大类" class CourseSubCategory(models.Model): """课程子类, e.g python linux """ category = models.ForeignKey("CourseCategory") name = models.CharField(max_length=64, unique=True) def __str__(self): return "%s" % self.name class Meta: verbose_name_plural = "02.课程子类" class DegreeCourse(models.Model): """学位课程""" name = models.CharField(max_length=128, unique=True) course_img = models.CharField(max_length=255, verbose_name="缩略图") brief = models.TextField(verbose_name="学位课程简介", ) total_scholarship = models.PositiveIntegerField(verbose_name="总奖学金(贝里)", default=40000) # 2000 2000 mentor_compensation_bonus = models.PositiveIntegerField(verbose_name="本课程的导师辅导费用(贝里)", default=15000) period = models.PositiveIntegerField(verbose_name="建议学习周期(days)", default=150) # 为了计算学位奖学金 prerequisite = models.TextField(verbose_name="课程先修要求", max_length=1024) teachers = models.ManyToManyField("Teacher", verbose_name="课程讲师") # 用于GenericForeignKey反向查询, 不会生成表字段,切勿删除 # coupon = GenericRelation("Coupon") # 用于GenericForeignKey反向查询,不会生成表字段,切勿删除 degreecourse_price_policy = GenericRelation("PricePolicy") def __str__(self): return self.name class Meta: verbose_name_plural = "03.学位课" class Teacher(models.Model): """讲师、导师表""" name = models.CharField(max_length=32) role_choices = ((0, '讲师'), (1, '导师')) role = models.SmallIntegerField(choices=role_choices, default=0) title = models.CharField(max_length=64, verbose_name="职位、职称") signature = models.CharField(max_length=255, help_text="导师签名", blank=True, null=True) image = models.CharField(max_length=128) brief = models.TextField(max_length=1024) def __str__(self): return self.name class Meta: verbose_name_plural = "04.导师或讲师" class Scholarship(models.Model): """学位课程奖学金""" degree_course = models.ForeignKey("DegreeCourse") time_percent = models.PositiveSmallIntegerField(verbose_name="奖励档位(时间百分比)", help_text="只填百分值,如80,代表80%") value = models.PositiveIntegerField(verbose_name="奖学金数额") def __str__(self): return "%s:%s" % (self.degree_course, self.value) class Meta: verbose_name_plural = "05.学位课奖学金" class Course(models.Model): """专题课/学位课模块表""" name = models.CharField(max_length=128, unique=True) course_img = models.CharField(max_length=255) sub_category = models.ForeignKey("CourseSubCategory") course_type_choices = ((0, '付费'), (1, 'VIP专享'), (2, '学位课程')) course_type = models.SmallIntegerField(choices=course_type_choices) # 不为空;学位课的某个模块 # 为空;专题课 degree_course = models.ForeignKey("DegreeCourse", blank=True, null=True, help_text="若是学位课程,此处关联学位表") brief = models.TextField(verbose_name="课程概述", max_length=2048) level_choices = ((0, '初级'), (1, '中级'), (2, '高级')) level = models.SmallIntegerField(choices=level_choices, default=1) pub_date = models.DateField(verbose_name="发布日期", blank=True, null=True) period = models.PositiveIntegerField(verbose_name="建议学习周期(days)", default=7) # order = models.IntegerField("课程顺序", help_text="从上一个课程数字往后排") attachment_path = models.CharField(max_length=128, verbose_name="课件路径", blank=True, null=True) status_choices = ((0, '上线'), (1, '下线'), (2, '预上线')) status = models.SmallIntegerField(choices=status_choices, default=0) template_id = models.SmallIntegerField("前端模板id", default=1) # coupon = GenericRelation("Coupon") # 用于GenericForeignKey反向查询,不会生成表字段,切勿删除 price_policy = GenericRelation("PricePolicy") asked_question = GenericRelation("OftenAskedQuestion") def __str__(self): return "%s(%s)" % (self.name, self.get_course_type_display()) def save(self, *args, **kwargs): if self.course_type == 2: if not self.degree_course: raise ValueError("学位课程必须关联对应的学位表") super(Course, self).save(*args, **kwargs) class Meta: verbose_name_plural = "06.专题课或学位课模块" class CourseDetail(models.Model): """课程详情页内容""" course = models.OneToOneField("Course") hours = models.IntegerField("课时") course_slogan = models.CharField(max_length=125, blank=True, null=True) video_brief_link = models.CharField(verbose_name='课程介绍', max_length=255, blank=True, null=True) 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 "%s" % self.course class Meta: verbose_name_plural = "07.课程或学位模块详细" 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: unique_together = ('content_type', 'object_id', 'question') verbose_name_plural = "08. 常见问题" class CourseOutline(models.Model): """课程大纲""" course_detail = models.ForeignKey("CourseDetail") 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: unique_together = ('course_detail', 'title') verbose_name_plural = "09. 课程大纲" class CourseChapter(models.Model): """课程章节""" course = models.ForeignKey("Course") chapter = models.SmallIntegerField(verbose_name="第几章", default=1) name = models.CharField(max_length=128) summary = models.TextField(verbose_name="章节介绍", blank=True, null=True) pub_date = models.DateField(verbose_name="发布日期", auto_now_add=True) class Meta: unique_together = ("course", 'chapter') verbose_name_plural = "10. 课程章节" def __str__(self): return "%s:(第%s章)%s" % (self.course, self.chapter, self.name) class CourseSection(models.Model): """课时目录""" chapter = models.ForeignKey("CourseChapter") name = models.CharField(max_length=128) order = models.PositiveSmallIntegerField(verbose_name="课时排序", help_text="建议每个课时之间空1至2个值,以备后续插入课时") 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") video_time = models.CharField(verbose_name="视频时长", blank=True, null=True, max_length=32) # 仅在前端展示使用 pub_date = models.DateTimeField(verbose_name="发布时间", auto_now_add=True) free_trail = models.BooleanField("是否可试看", default=False) class Meta: unique_together = ('chapter', 'section_link') verbose_name_plural = "11. 课时" def __str__(self): return "%s-%s" % (self.chapter, self.name) class Homework(models.Model): chapter = models.ForeignKey("CourseChapter") title = models.CharField(max_length=128, verbose_name="作业题目") order = models.PositiveSmallIntegerField("作业顺序", help_text="同一课程的每个作业之前的order值间隔1-2个数") homework_type_choices = ((0, '作业'), (1, '模块通关考核')) homework_type = models.SmallIntegerField(choices=homework_type_choices, default=0) requirement = models.TextField(max_length=1024, verbose_name="作业需求") threshold = models.TextField(max_length=1024, verbose_name="踩分点") recommend_period = models.PositiveSmallIntegerField("推荐完成周期(天)", default=7) scholarship_value = models.PositiveSmallIntegerField("为该作业分配的奖学金(贝里)") note = models.TextField(blank=True, null=True) enabled = models.BooleanField(default=True, help_text="本作业如果后期不需要了,不想让学员看到,可以设置为False") class Meta: unique_together = ("chapter", "title") verbose_name_plural = "12. 章节作业" def __str__(self): return "%s - %s" % (self.chapter, self.title) # class CourseReview(models.Model): # """课程评价""" # enrolled_course = models.OneToOneField("EnrolledCourse") # about_teacher = models.FloatField(default=0, verbose_name="讲师讲解是否清晰") # about_video = models.FloatField(default=0, verbose_name="内容实用") # about_course = models.FloatField(default=0, verbose_name="课程内容通俗易懂") # review = models.TextField(max_length=1024, verbose_name="评价") # disagree_number = models.IntegerField(default=0, verbose_name="踩") # agree_number = models.IntegerField(default=0, verbose_name="赞同数") # tags = models.ManyToManyField("Tags", blank=True, verbose_name="标签") # date = models.DateTimeField(auto_now_add=True, verbose_name="评价日期") # is_recommend = models.BooleanField("热评推荐", default=False) # hide = models.BooleanField("不在前端页面显示此条评价", default=False) # # def __str__(self): # return "%s-%s" % (self.enrolled_course.course, self.review) # # class Meta: # verbose_name_plural = "13. 课程评价(购买课程后才能评价)" # # # class DegreeCourseReview(models.Model): # """学位课程评价 # 为了以后可以定制单独的评价内容,所以不与普通课程的评价混在一起,单独建表 # """ # enrolled_course = models.ForeignKey("EnrolledDegreeCourse") # course = models.ForeignKey("Course", verbose_name="评价学位模块", blank=True, null=True, # help_text="不填写即代表评价整个学位课程", limit_choices_to={'course_type': 2}) # about_teacher = models.FloatField(default=0, verbose_name="讲师讲解是否清晰") # about_video = models.FloatField(default=0, verbose_name="视频质量") # about_course = models.FloatField(default=0, verbose_name="课程") # review = models.TextField(max_length=1024, verbose_name="评价") # disagree_number = models.IntegerField(default=0, verbose_name="踩") # agree_number = models.IntegerField(default=0, verbose_name="赞同数") # tags = models.ManyToManyField("Tags", blank=True, verbose_name="标签") # date = models.DateTimeField(auto_now_add=True, verbose_name="评价日期") # is_recommend = models.BooleanField("热评推荐", default=False) # hide = models.BooleanField("不在前端页面显示此条评价", default=False) # # def __str__(self): # return "%s-%s" % (self.enrolled_course, self.review) # # class Meta: # verbose_name_plural = "14. 学位课评价(购买课程后才能评价)" class PricePolicy(models.Model): """价格与有课程效期表""" content_type = models.ForeignKey(ContentType) # 关联course or degree_course object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') # course = models.ForeignKey("Course") valid_period_choices = ((1, '1天'), (3, '3天'), (7, '1周'), (14, '2周'), (30, '1个月'), (60, '2个月'), (90, '3个月'), (180, '6个月'), (210, '12个月'), (540, '18个月'), (720, '24个月'), ) valid_period = models.SmallIntegerField(choices=valid_period_choices) price = models.FloatField() class Meta: unique_together = ("content_type", 'object_id', "valid_period") verbose_name_plural = "15. 价格策略" def __str__(self): return "%s(%s)%s" % (self.content_object, self.get_valid_period_display(), self.price)
下载数据库使用(务必下载,上面的压缩包数据库是空的!!!)
https://github.com/987334176/luffycity/blob/master/db.sqlite3
修改api应用下的api_urls.py
from django.conf.urls import url from api import views urlpatterns = [ url(r'index/$', views.CheckView.as_view()), ]
a
查看所有学位课并打印学位课名称以及授课老师
修改api应用下的views.py
from django.shortcuts import render,HttpResponse from api import models from django.views import View # Create your views here. class CheckView(View): """ 练习题相关 """ def get(self, request): # a.查看所有学位课并打印学位课名称以及授课老师 degree_list = models.DegreeCourse.objects.all().values('name', 'teachers__name') print(degree_list) return HttpResponse('ok')
访问网页:http://127.0.0.1:8000/api/index/
查看控制台输出:
<QuerySet [{'teachers__name': '李泳谊', 'name': 'Linux自动化运维 · 中级'}, {'teachers__name': 'Alex 金角大王', 'name': 'Python全栈开发 · 中级'}]>
第二种写法:
class CheckView(View): """ 练习题相关 """ def get(self, request): # a.查看所有学位课并打印学位课名称以及授课老师 queryset = models.DegreeCourse.objects.all() for row in queryset: print(row.name,row.teachers.all()) return HttpResponse('ok')
查看控制台输出:
<QuerySet [{'teachers__name': '李泳谊', 'name': 'Linux自动化运维 · 中级'}, {'teachers__name': 'Alex 金角大王', 'name': 'Python全栈开发 · 中级'}]>
b
查看所有学位课并打印学位课名称以及学位课的奖学金
class CheckView(View): """ 练习题相关 """ def get(self, request): # b.查看所有学位课并打印学位课名称以及学位课的奖学金 c_obj=models.DegreeCourse.objects.all() for i in c_obj: # 利用degreecourse_price_policy字段进行反向查询 print(i.name,i.degreecourse_price_policy.all().values('price')) return HttpResponse('ok')
刷新网页,查看控制台输出:
Python全栈开发 · 中级 <QuerySet [{'price': 10.0}]> Linux自动化运维 · 中级 <QuerySet [{'price': 50.0}]>
第二种写法:
class CheckView(View): """ 练习题相关 """ def get(self, request): # b.查看所有学位课并打印学位课名称以及学位课的奖学金 degree_list = models.DegreeCourse.objects.all() for row in degree_list: print(row.name) scholarships = row.scholarship_set.all() for item in scholarships: print('------>',item.time_percent,item.value)
执行输出:
Python全栈开发 · 中级 ------> 50 1000 Linux自动化运维 · 中级 ------> 50 1000
c
展示所有的专题课
class CheckView(View): """ 练习题相关 """ def get(self, request): # c. 展示所有的专题课 c_obj = models.Course.objects.filter(degree_course__isnull=True) print(c_obj) return HttpResponse('ok')
刷新网页,查看控制台输出:
<QuerySet [<Course: Linux基础(付费)>]>
d
查看id=1的学位课对应的所有模块名称
class CheckView(View): """ 练习题相关 """ def get(self, request): # d. 查看id=1的学位课对应的所有模块名称 obj = models.DegreeCourse.objects.filter(id=1).values('course__name') print(obj) return HttpResponse('ok')
刷新网页,查看控制台输出:
<QuerySet [<Course: Linux基础(付费)>]>
e
获取id = 1的专题课,并打印:课程名、级别(中文)、why_study、what_to_study_brief、所有recommend_courses
class CheckView(View): """ 练习题相关 """ def get(self, request): # e. 获取id = 1的专题课,并打印:课程名、级别(中文)、why_study、what_to_study_brief、所有recommend_courses c_obj = models.Course.objects.filter(id=1) print(c_obj.values('name')) print(c_obj.first().get_level_display()) print(c_obj.values('coursedetail__why_study')) print(c_obj.values('coursedetail__what_to_study_brief')) print(c_obj.values('coursedetail__recommend_courses')) return HttpResponse('ok')
刷新网页,查看控制台输出:
<QuerySet [{'name': 'Python开发入门7天特训营'}]> 初级 <QuerySet [{'coursedetail__why_study': '适合零基础学编程的小白,语言简洁高效,为进入人工智能、数据分析和网站开发做好准备;如果你准备敲下你人生的第一行代码、写出你的第一个程序,路飞学城的入门特训营会是最好的选择'}]> <QuerySet [{'coursedetail__what_to_study_brief': '1、Python如何才能掌握真正的要领;\r\n2、看完理论课程自己怎么开始动手写出代码;\r\n3、什么才是专业的代码;\r\n4、最流行Python编程工具使用技巧;\r\n5、如何系统且高效的学习Python,遇到问题该如何入手分析\r\n6、可以自主开发各种小程序(如分数打印、猜年龄、用户登录程序、三级菜单等)'}]> <QuerySet [{'coursedetail__recommend_courses': 1}]>
第二种写法:
Python开发入门7天特训营 适合零基础学编程的小白,语言简洁高效,为进入人工智能、数据分析和网站开发做好准备;如果你准备敲下你人生的第一行代码、写出你的第一个程序,路飞学城的入门特训营会是最好的选择 初级 7 适合零基础学编程的小白,语言简洁高效,为进入人工智能、数据分析和网站开发做好准备;如果你准备敲下你人生的第一行代码、写出你的第一个程序,路飞学城的入门特训营会是最好的选择 <QuerySet [<Course: Python开发入门7天特训营(付费)>]>
刷新网页,查看控制台输出:
Python开发入门7天特训营 适合零基础学编程的小白,语言简洁高效,为进入人工智能、数据分析和网站开发做好准备;如果你准备敲下你人生的第一行代码、写出你的第一个程序,路飞学城的入门特训营会是最好的选择 初级 7 适合零基础学编程的小白,语言简洁高效,为进入人工智能、数据分析和网站开发做好准备;如果你准备敲下你人生的第一行代码、写出你的第一个程序,路飞学城的入门特训营会是最好的选择 <QuerySet [<Course: Python开发入门7天特训营(付费)>]>
注意:显示choices的中文,使用get_字段名_display() 就可以了!
f
获取id = 1的专题课,并打印该课程相关的所有常见问题
class CheckView(View): """ 练习题相关 """ def get(self, request): # f. 获取id = 1的专题课,并打印该课程相关的所有常见问题 c_obj = models.Course.objects.filter(id=1).first() # 利用asked_question字段进行反向查询 print(c_obj.asked_question.all().values('question')) return HttpResponse('ok')
刷新网页,查看控制台输出:
<QuerySet [{'question': '我是零基础,能学会吗?'}, {'question': '需要学习多长时间?'}]>
第二种写法:
class CheckView(View): """ 练习题相关 """ def get(self, request): # f. 获取id = 1的专题课,并打印该课程相关的所有常见问题 obj = models.Course.objects.get(id=1) ask_list = obj.asked_question.all() for item in ask_list: print(item.question) return HttpResponse('ok')
刷新网页,查看控制台输出:
我是零基础,能学会吗?
需要学习多长时间?
g
获取id = 1的专题课,并打印该课程相关的课程大纲
class CheckView(View): """ 练习题相关 """ def get(self, request): # g.获取id = 1的专题课,并打印该课程相关的课程大纲 c_obj = models.Course.objects.filter(id=1) print(c_obj.values('coursedetail__courseoutline__title')) return HttpResponse('ok')
刷新网页,查看控制台输出:
<QuerySet [{'coursedetail__courseoutline__title': 'Python基础知识'}, {'coursedetail__courseoutline__title': 'Python数据类型初识'}]>
第二种写法:
class CheckView(View): """ 练习题相关 """ def get(self, request): # g.获取id = 1的专题课,并打印该课程相关的课程大纲 obj = models.Course.objects.get(id=1) outline_list = obj.coursedetail.courseoutline_set.all() for item in outline_list: print(item.title,item.content) return HttpResponse('ok')
刷新网页,查看控制台输出:
Python基础知识 Python基础知识、开营直播课
Python数据类型初识 Python数据类型初识、流程控制
h
获取id = 1的专题课,并打印该课程相关的所有章节
class CheckView(View): """ 练习题相关 """ def get(self, request): # h.获取id = 1的专题课,并打印该课程相关的所有章节 c_obj = models.Course.objects.filter(id=1) print(c_obj.values('coursechapter__name')) return HttpResponse('ok')
刷新网页,查看控制台输出:
<QuerySet [{'coursechapter__name': 'Python基础知识'}, {'coursechapter__name': 'Python数据类型初识'}]>
第二种写法,使用反向查询
class CheckView(View): """ 练习题相关 """ def get(self, request): # h.获取id = 1的专题课,并打印该课程相关的所有章节 obj = models.Course.objects.get(id=1) chapter_list = obj.coursechapter_set.all() # 默认obj.表名_set.all() for item in chapter_list: print(item.name) return HttpResponse('ok')
刷新网页,查看控制台输出:
Python基础知识
Python数据类型初识
i
获取id = 1的专题课,并打印该课程相关的所有课时
class CheckView(View): """ 练习题相关 """ def get(self, request): # i.获取id = 1的专题课,并打印该课程相关的所有课时 c_obj = models.Course.objects.filter(id=1) for i in c_obj.values('coursechapter__chapter','coursechapter__name'): print(i.get('coursechapter__chapter'),i.get('coursechapter__name')) a_obj=models.CourseChapter.objects.filter(name=i.get('coursechapter__name')) for j in a_obj.values('coursesection__name'): print(' ',j.get('coursesection__name')) return HttpResponse('ok')
刷新网页,查看控制台输出:
1 Python基础知识 python基础 2 Python数据类型初识 数据类型
第二种写法:
class CheckView(View): """ 练习题相关 """ def get(self, request): # i.获取id = 1的专题课,并打印该课程相关的所有课时 obj = models.Course.objects.get(id=1) chapter_list = obj.coursechapter_set.all() for chapter in chapter_list: print(chapter.name,chapter.coursesection_set.all()) return HttpResponse('ok')
刷新网页,查看控制台输出:
Python数据类型初识 <QuerySet [<CourseSection: Python开发入门7天特训营(付费):(第2章)Python数据类型初识-数据类型>]>
所有视图都集中在views.py中,扩展不方便。
删除views.py,并创建目录views
在views目录里面创建course.py,它表示和课程相关的视图
注意:视图名最好加上View,这是约定俗成的
from django.shortcuts import render,HttpResponse from api import models from rest_framework.views import APIView class CourseView(APIView): def get(self,request,*args,**kwargs): return HttpResponse('ok')
修改api_urls.py
from django.conf.urls import url from api.views import course urlpatterns = [ url(r'course/$', course.CourseView.as_view()), ]
访问网页:http://127.0.0.1:8000/api/course/
二、DRF版本
在Django rest-framework中提供了5中version设置方式
基于url的get传参方式
比如:/course?version=v1
settings.py,最后一行添加。这里面规定了版本为v1和v2,如果是其他版本,会报404错误!
REST_FRAMEWORK = { 'DEFAULT_VERSION': 'v1', # 默认版本 'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本 'VERSION_PARAM': 'version' # URL中获取值的key }
api_urls.py
from django.conf.urls import url from api.views import course urlpatterns = [ url(r'course/$', course.CourseView.as_view(),name='test'), ]
course.py
from django.shortcuts import render,HttpResponse from api import models from rest_framework.views import APIView from rest_framework.versioning import QueryParameterVersioning from rest_framework.response import Response class CourseView(APIView): # 开启版本支持功能 versioning_class = QueryParameterVersioning def get(self, request, *args, **kwargs): # 获取版本 print(request.version) # 获取版本管理的类 print(request.versioning_scheme) # 反向生成URL reverse_url = request.versioning_scheme.reverse('test', request=request) print(reverse_url) return Response('GET请求,响应内容')
访问网页: http://127.0.0.1:8000/api/course/
查看Pycharm控制台输出:
v1 <rest_framework.versioning.QueryParameterVersioning object at 0x000001AAE5245F28> http://127.0.0.1:8000/api/course/?version=v1
基于url的正则方式(推荐)
比如:/v1/users/
settings.py,保持不变
api_urls.py
from django.conf.urls import url from api.views import course urlpatterns = [ url(r'^(?P<version>[v1|v2]+)/course/$', course.CourseView.as_view(), name='test'), ]
course.py,修改versioning_class
from django.shortcuts import render,HttpResponse from api import models from rest_framework.views import APIView from rest_framework.versioning import URLPathVersioning from rest_framework.response import Response class CourseView(APIView): # 开启版本支持功能 versioning_class = URLPathVersioning def get(self, request, *args, **kwargs): # 获取版本 print(request.version) # 获取版本管理的类 print(request.versioning_scheme) # 反向生成URL reverse_url = request.versioning_scheme.reverse('test', request=request) print(reverse_url) return Response('GET请求,响应内容')
访问网页: http://127.0.0.1:8000/api/v1/course/
注意要带版本,否则报错
查看Pycharm控制台输出:
v1 <rest_framework.versioning.URLPathVersioning object at 0x00000200F0F2E4A8> http://127.0.0.1:8000/api/v1/course/
基于 accept 请求头方式
比如:Accept: application/json; version=1.0
老外喜欢用这种方法,因为比较安全
settings.py,保持不变
api_urls.py
from django.conf.urls import url from api.views import course urlpatterns = [ url(r'^course/$', course.CourseView.as_view(), name='test'), ]
course.py,修改versioning_class
from django.shortcuts import render,HttpResponse from api import models from rest_framework.views import APIView from rest_framework.versioning import AcceptHeaderVersioning from rest_framework.response import Response class CourseView(APIView): # 开启版本支持功能 versioning_class = AcceptHeaderVersioning def get(self, request, *args, **kwargs): # 获取版本 HTTP_ACCEPT头 print(request.version) # 获取版本管理的类 print(request.versioning_scheme) # 反向生成URL reverse_url = request.versioning_scheme.reverse('test', request=request) print(reverse_url) return Response('GET请求,响应内容')
使用postman发送get请求,增加一个头version
查看Pycharm控制台输出:
v1 <rest_framework.versioning.AcceptHeaderVersioning object at 0x000001AEA89206D8> http://127.0.0.1:8000/api/course/
基于主机名方式
比如:v1.example.com
settings.py,允许所有主机访问
ALLOWED_HOSTS = ['*']
api_urls.py,保持不变
course.py,修改versioning_class
from django.shortcuts import render,HttpResponse from api import models from rest_framework.views import APIView from rest_framework.versioning import HostNameVersioning from rest_framework.response import Response class CourseView(APIView): # 开启版本支持功能 versioning_class = HostNameVersioning def get(self, request, *args, **kwargs): # 获取版本 print(request.version) # 获取版本管理的类 print(request.versioning_scheme) # 反向生成URL reverse_url = request.versioning_scheme.reverse('test', request=request) print(reverse_url) return Response('GET请求,响应内容')
修改本机的hosts文件,增加2条记录
127.0.0.1 v1.example.com
127.0.0.1 v2.example.com
访问url: http://v1.example.com:8000/api/course/
查看Pycharm控制台输出:
v1 <rest_framework.versioning.HostNameVersioning object at 0x000001DE71A00470> http://127.0.0.1:8000/api/course/
访问url: http://v2.example.com:8000/api/course/
查看Pycharm控制台输出:
v2 <rest_framework.versioning.HostNameVersioning object at 0x000001B31D5C3E48> http://v2.example.com:8000/api/course/
基于django路由系统的namespace
比如:example.com/v1/users/
settings.py,保持不变
api_urls.py
from django.conf.urls import url from api.views import course urlpatterns = [ url(r'^v1/', ([url(r'course/', course.CourseView.as_view(), name='test'), ], None, 'v1')), url(r'^v2/', ([url(r'course/', course.CourseView.as_view(), name='test'), ], None, 'v2')), ]
course.py
from django.shortcuts import render,HttpResponse from api import models from rest_framework.views import APIView from rest_framework.versioning import NamespaceVersioning from rest_framework.response import Response class CourseView(APIView): # 开启版本支持功能 versioning_class = NamespaceVersioning def get(self, request, *args, **kwargs): # 获取版本 print(request.version) # 获取版本管理的类 print(request.versioning_scheme) # 反向生成URL reverse_url = request.versioning_scheme.reverse('test', request=request) print(reverse_url) return Response('GET请求,响应内容')
访问url: http://127.0.0.1:8000/api/v1/course/
查看Pycharm控制台输出:
v1 <rest_framework.versioning.NamespaceVersioning object at 0x0000020BC5FAFDA0> http://127.0.0.1:8000/api/v1/course/
全局使用方式
settings.py
REST_FRAMEWORK = { 'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning", 'DEFAULT_VERSION': 'v1', 'ALLOWED_VERSIONS': ['v1', 'v2'], 'VERSION_PARAM': 'version' }
api_urls.py
from django.conf.urls import url from api.views import course urlpatterns = [ url(r'^course/', course.CourseView.as_view(), name='test'), ]
course.py
from django.shortcuts import render,HttpResponse from api import models from rest_framework.views import APIView # from rest_framework.versioning import NamespaceVersioning from rest_framework.response import Response class CourseView(APIView): # 开启版本支持功能 # versioning_class = NamespaceVersioning def get(self, request, *args, **kwargs): # 获取版本 print(request.version) # 获取版本管理的类 print(request.versioning_scheme) # # 反向生成URL # reverse_url = request.versioning_scheme.reverse('test', request=request) # print(reverse_url) return Response('GET请求,响应内容')
访问url: http://127.0.0.1:8000/api/course/
查看Pycharm控制台输出:
v1
<rest_framework.versioning.URLPathVersioning object at 0x000001FF069907F0>
默认版本为v1
源码分析,请参考链接:
http://www.cnblogs.com/derek1184405959/p/8724270.html
总结:在认证,权限,节流之前做了版本处理
API编写
先做专题课
第一版,json
from django.shortcuts import render,HttpResponse from api import models from rest_framework.views import APIView from rest_framework.response import Response import json class CourseView(APIView): def get(self, request, *args, **kwargs): """ 获取所有专题课信息 :param request: :param args: :param kwargs: :return: """ # 方式一: course_list = list(models.Course.objects.all().values('id','name')) return HttpResponse(json.dumps(course_list,ensure_ascii=False))
访问页面: http://127.0.0.1:8000/api/course/
第二版,序列化组件
在api应用目录下创建serializers文件夹,在里面创建course.py
from rest_framework import serializers class CourseSerializer(serializers.Serializer): id = serializers.IntegerField() name = serializers.CharField()
修改views目录下的course.py
from django.shortcuts import render,HttpResponse from api import models from rest_framework.views import APIView from rest_framework.response import Response from api.serializers.course import CourseSerializer class CourseView(APIView): def get(self, request, *args, **kwargs): """ 获取所有专题课信息 :param request: :param args: :param kwargs: :return: """ # 方式二: course_list = models.Course.objects.all() ser = CourseSerializer(instance=course_list,many=True) return Response(ser.data)
刷新页面,效果如下:
三、DRF分页
如果当数据量特别大的时候,需要使用分页
REST框架支持自定义分页风格,如果使用的是APIView,你就需要自己调用分页API,确保返回一个分页后的响应。
指定每页大小
修改views目录下course.py
from django.shortcuts import render,HttpResponse from api import models from rest_framework.views import APIView from rest_framework.response import Response from api.serializers.course import CourseSerializer from rest_framework.pagination import PageNumberPagination class CourseView(APIView): def get(self, request, *args, **kwargs): # 从数据库中获取数据 queryset = models.Course.objects.all() # 分页 page = PageNumberPagination() # paginate_queryset 接收3参数,分别是queryset对象,request,视图 # 这里的self就是CourseView视图 course_list = page.paginate_queryset(queryset,request,self) # 分页之后的结果执行序列化 ser = CourseSerializer(instance=course_list,many=True) return Response(ser.data)
修改settings.py,指定每页大小。这里表示每页一条数据
REST_FRAMEWORK = { 'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning", 'DEFAULT_VERSION': 'v1', 'ALLOWED_VERSIONS': ['v1', 'v2'], 'VERSION_PARAM': 'version', 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE':1 }
访问网页: http://127.0.0.1:8000/api/course/
效果如下:
获取下一页
怎么获取下一页呢?在url上面加一个参数page=页码数,比如:
http://127.0.0.1:8000/api/course/?page=2
效果如下
为什么参数是page呢?而不是其他的呢?看下面的源码
class PageNumberPagination(BasePagination): """ A simple page number based style that supports page numbers as query parameters. For example: http://api.example.org/accounts/?page=4 http://api.example.org/accounts/?page=4&page_size=100 """ # The default page size. # Defaults to `None`, meaning pagination is disabled. page_size = api_settings.PAGE_SIZE django_paginator_class = DjangoPaginator # Client can control the page using this query parameter. page_query_param = 'page' page_query_description = _('A page number within the paginated result set.') # Client can control the page size using this query parameter. # Default is 'None'. Set to eg 'page_size' to enable usage. page_size_query_param = None page_size_query_description = _('Number of results to return per page.') # Set to an integer to limit the maximum page size the client may request. # Only relevant if 'page_size_query_param' has also been set. max_page_size = None last_page_strings = ('last',) template = 'rest_framework/pagination/numbers.html' invalid_page_message = _('Invalid page.')
看这一行
page_query_param = 'page'
它指定了url参数为page
返回code
这里有一个问题,如果连接不上数据,就会报错
所以需要使用异常判断,定义一个字典,返回code
1000表示成功,500表示错误。备注:这个是自己定义的,看你的心情了!
修改views下的course.py
from django.shortcuts import render,HttpResponse from api import models from rest_framework.views import APIView from rest_framework.response import Response from api.serializers.course import CourseSerializer from rest_framework.pagination import PageNumberPagination class CourseView(APIView): def get(self, request, *args, **kwargs): response = {'code':1000,'data':None,'error':None} try: # 从数据库中获取数据 queryset = models.Course.objects.all() # 分页 page = PageNumberPagination() # paginate_queryset 接收3参数,分别是queryset对象,request,视图 # 这里的self就是CourseView视图 course_list = page.paginate_queryset(queryset,request,self) # 分页之后的结果执行序列化 ser = CourseSerializer(instance=course_list,many=True) response['data'] = ser.data except Exception as e: response['code'] = 500 response['error'] = '获取数据失败' return Response(response)
访问页面,发现数据多了几个key
模拟出错
修改views下的course.py,将ORM语句故意改错
from django.shortcuts import render,HttpResponse from api import models from rest_framework.views import APIView from rest_framework.response import Response from api.serializers.course import CourseSerializer from rest_framework.pagination import PageNumberPagination class CourseView(APIView): def get(self, request, *args, **kwargs): response = {'code':1000,'data':None,'error':None} try: # 从数据库中获取数据 queryset = models.Course.objects.allxx() # 模拟错误 # 分页 page = PageNumberPagination() # paginate_queryset 接收3参数,分别是queryset对象,request,视图 # 这里的self就是CourseView视图 course_list = page.paginate_queryset(queryset,request,self) # 分页之后的结果执行序列化 ser = CourseSerializer(instance=course_list,many=True) response['data'] = ser.data except Exception as e: response['code'] = 500 response['error'] = '获取数据失败' return Response(response)
刷新页面,提示失败
注意:每一个接口,一定要加try,防止程序崩溃
在公司里面写代码,必须这样。因为线上环境比较复杂,有各自攻击行为存在。所以,必须写异常判断。
而且,需要将异常信息写入日志。方便后续的排错,因为线上的代码,不能直接print,影响业务!
定义code类
这个视图定义了一个字典,用来做初始状态返回。那么其他视图,也需要返回code,再定义一遍?
如果有100个呢?所以,需要为这个功能,单独定义一个类。
这种是通用类,我们一般会放在应用(这里指的是api)下的utils(工具)文件夹中。
在api目录下创建utils文件夹,新建文件response.py
class BaseResponse(object): def __init__(self): self.code = 1000 self.data = None self.error = None @property # 负责把一个方法变成属性调用 def dict(self): return self.__dict__
python中的类,都会从object里继承一个__dict__属性,这个属性中存放着类的属性和方法对应的键值对
修改views下的course.py,导入response
from django.shortcuts import render,HttpResponse from api import models from rest_framework.views import APIView from rest_framework.response import Response from api.serializers.course import CourseSerializer from rest_framework.pagination import PageNumberPagination from api.utils.response import BaseResponse class CourseView(APIView): def get(self, request, *args, **kwargs): # response = {'code':1000,'data':None,'error':None} ret = BaseResponse() try: # 从数据库中获取数据 queryset = models.Course.objects.all() # 分页 page = PageNumberPagination() # paginate_queryset 接收3参数,分别是queryset对象,request,视图 # 这里的self就是CourseView视图 course_list = page.paginate_queryset(queryset,request,self) # 分页之后的结果执行序列化 ser = CourseSerializer(instance=course_list,many=True) ret.data = ser.data except Exception as e: ret.code = 500 ret.error = '获取数据失败' return Response(ret.__dict__)
刷新页面,效果如下:
第二版,到这里,就结束了。推荐使用第二种!
四、DRF序列化进阶
ModelSerializer
通常你会想要与Django模型相对应的序列化类。
ModelSerializer
类能够让你自动创建一个具有模型中相应字段的Serializer
类。
这个ModelSerializer
类和常规的Serializer
类一样,不同的是:
- 它根据模型自动生成一组字段。
- 它自动生成序列化器的验证器,比如unique_together验证器。
- 它默认简单实现了
.create()
方法和.update()
方法。
查看serializers目录下course.py,这里我是手动指定了要序列化的字段,比如id和name
但是,还有更方便的方法。使用ModelSerializer,它会自动和ORM表关联。
修改serializers目录下course.py
from rest_framework import serializers from api import models class CourseSerializer(serializers.ModelSerializer): # id = serializers.IntegerField() # name = serializers.CharField() class Meta: # 配置项 model = models.Course # Course表 fields = ['id','name'] # 指定序列化的字段
默认情况下,所有的模型的字段都将映射到序列化器上相应的字段。
模型中任何关联字段比如外键都将映射到PrimaryKeyRelatedField
字段。默认情况下不包括反向关联,除非像serializer relations文档中规定的那样显示包含。
刷新页面,效果同上!
Model.get_FOO_display
查看官方文档
https://docs.djangoproject.com/en/dev/ref/models/instances/#django.db.models.Model.get_FOO_display
对于具有选择集的每个字段,该对象将具有一个get_FOO_display()方法,其中FOO是该字段的名称。 此方法返回字段的“可读”值。
查看models.py里面的Course类,看下面这2行
level_choices = ((0, '初级'), (1, '中级'), (2, '高级')) level = models.SmallIntegerField(choices=level_choices, default=1)
level_choices 它只是一个变量而已,上面2行,可以合并为一行
level = models.SmallIntegerField(choices=((0, '初级'), (1, '中级'), (2, '高级')), default=1)
真正在数据库中,生成字段的是level。choices=xxx,表示它是一个选择集。真正在数据库存储的是1,后面的中文,主要是在前端展示的。这样做的目的,是为了节省磁盘空间。一般数据库用utf-8,一个中文占用3字节。而数字只占用1字节!
那么前端如何显示中文呢?使用get_level_display() ,注意:level是数据库的字段,值的类型必须是choices
修改serializers目录下course.py
from rest_framework import serializers from api import models class CourseSerializer(serializers.ModelSerializer): # 显示choices里面的中文 level_cn = serializers.CharField(source='get_level_display') class Meta: # 配置项 model = models.Course # Course表 fields = ['id','name','level_cn'] # 指定序列化的字段
注意:level_cn只是一个变量名而已,约定俗成会定义为字段名。这里的get_level_display不需要加括号,它会自动执行!
刷新页面,效果如下:
一对一
Course和CourseDetail是一对一关系
现在需要显示课时,但是课时在另外一个表(CourseDetail课程详情表)中,可以用双下方法吗?
fields = ['id','name','level_cn','coursedetail__hours']
刷新页面,会报错
针对一对一的跨表查询,可以使用表名小写.字段名
修改serializers目录下course.py
from rest_framework import serializers from api import models class CourseSerializer(serializers.ModelSerializer): # 显示choices里面的中文 level_cn = serializers.CharField(source='get_level_display') # 显示课时,对于一对一,使用表名.字段 hours = serializers.CharField(source='coursedetail.hours') class Meta: # 配置项 model = models.Course # Course表 fields = ['id','name','level_cn','hours'] # 指定序列化的字段
重启django项目,刷新页面
在Pycharm控制台,会有一个警告信息
UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list:
百度翻译一下:
unOrdEdObjistList警告:分页可能与无序的对象列表产生不一致的结果:
啥意思呢?大概意思就是分页时,没有给它指定排序规则
修改views下的course.py,增加一个order_by就可以了
order_by('pk'),表示以主键排序,默认是升序
from django.shortcuts import render,HttpResponse from api import models from rest_framework.views import APIView from rest_framework.response import Response from api.serializers.course import CourseSerializer from rest_framework.pagination import PageNumberPagination from api.utils.response import BaseResponse class CourseView(APIView): def get(self, request, *args, **kwargs): # response = {'code':1000,'data':None,'error':None} ret = BaseResponse() try: # 从数据库中获取数据 queryset = models.Course.objects.all().order_by('pk') # 分页 page = PageNumberPagination() # paginate_queryset 接收3参数,分别是queryset对象,request,视图 # 这里的self就是CourseView视图 course_list = page.paginate_queryset(queryset,request,self) # 分页之后的结果执行序列化 ser = CourseSerializer(instance=course_list,many=True) ret.data = ser.data except Exception as e: ret.code = 500 ret.error = '获取数据失败' return Response(ret.__dict__)
重启django项目,刷新页面。再次查看Pycharm控制台,就没有警告了!
多对多
CourseDetail表的recommend_courses字段和Course表,是多对多的关系
如果要显示recommend_courses(推荐课程)呢?可以使用表名小写.字段名吗?测试一下
修改serializers目录下course.py
from rest_framework import serializers from api import models class CourseSerializer(serializers.ModelSerializer): # 显示choices里面的中文 level_cn = serializers.CharField(source='get_level_display') # 显示课时,对于一对一,使用表名.字段 hours = serializers.CharField(source='coursedetail.hours') recommend_courses = serializers.CharField(source='coursedetail.recommend_courses') class Meta: # 配置项 model = models.Course # Course表 fields = ['id','name','level_cn','hours','recommend_courses'] # 指定序列化的字段
刷新页面,效果如下:
发现recommend_courses字段,并不是我们想要的结果
再加一个.all
from rest_framework import serializers from api import models class CourseSerializer(serializers.ModelSerializer): # 显示choices里面的中文 level_cn = serializers.CharField(source='get_level_display') # 显示课时,对于一对一,使用表名.字段 hours = serializers.CharField(source='coursedetail.hours') recommend_courses = serializers.CharField(source='coursedetail.recommend_courses.all') class Meta: # 配置项 model = models.Course # Course表 fields = ['id','name','level_cn','hours','recommend_courses'] # 指定序列化的字段
刷新页面,效果如下:
发现它是一个queryset对象,如何获取对象里面的属性呢?
get_{field_name}
它是用来获取别的表字段的。
查看serializers目录下course.py,点击CharField。搜索关键字get_{field_name},看源码
def bind(self, field_name, parent): # In order to enforce a consistent style, we error if a redundant # 'method_name' argument has been used. For example: # my_field = serializer.SerializerMethodField(method_name='get_my_field') default_method_name = 'get_{field_name}'.format(field_name=field_name) assert self.method_name != default_method_name, ( "It is redundant to specify `%s` on SerializerMethodField '%s' in " "serializer '%s', because it is the same as the default method name. " "Remove the `method_name` argument." % (self.method_name, field_name, parent.__class__.__name__) ) # The method name should default to `get_{field_name}`. if self.method_name is None: self.method_name = default_method_name super(SerializerMethodField, self).bind(field_name, parent)
修改serializers目录下course.py,专门定义一个方法,用来获取recommend_courses
from rest_framework import serializers from api import models class CourseSerializer(serializers.ModelSerializer): # 显示choices里面的中文 level_cn = serializers.CharField(source='get_level_display') # 显示课时,对于一对一,使用表名.字段 hours = serializers.CharField(source='coursedetail.hours') # 显示推荐课程,对于多对多,使用def_字段名 # SerializerMethodField表示序列化字段,使用方法获取 recommend_courses = serializers.SerializerMethodField() class Meta: # 配置项 model = models.Course # Course表 fields = ['id','name','level_cn','hours','recommend_courses'] # 指定序列化的字段 def get_recommend_courses(self,row): recommend_list = [] # 空列表 # row表示一行数据, .all()表示取所有关联的书籍 for item in row.coursedetail.recommend_courses.all(): recommend_list.append({'id':item.id,'name':item.name}) return recommend_list
重启2遍django项目,刷新页面,效果如下:
上面代码中的,列表里面添加字典数据,可以改成列表生成
from rest_framework import serializers from api import models class CourseSerializer(serializers.ModelSerializer): # 显示choices里面的中文 level_cn = serializers.CharField(source='get_level_display') # 显示课时,对于一对一,使用表名.字段 hours = serializers.CharField(source='coursedetail.hours') # 显示推荐课程,对于多对多,使用def_字段名 # SerializerMethodField表示序列化字段,使用方法获取 recommend_courses = serializers.SerializerMethodField() class Meta: # 配置项 model = models.Course # Course表 fields = ['id','name','level_cn','hours','recommend_courses'] # 指定序列化的字段 def get_recommend_courses(self,row): recommend_list = row.coursedetail.recommend_courses.all() return [{'id': item.id, 'name': item.name} for item in recommend_list]
重启django项目,刷新页面,效果同上!
看下图中的代码
这个名字,必须一一对应,否则报错
因为源码里面定义了,它是取get_字段名方法的。
SerializerMethodField 拆分成3个单词:Serializer Method Field。可以理解为序列化,方法,字段
一对多
Course表的degree_course字段和DegreeCourse表示一对多关系
要显示总奖学金
修改serializers目录下course.py
from rest_framework import serializers from api import models class CourseSerializer(serializers.ModelSerializer): # 显示choices里面的中文 level_cn = serializers.CharField(source='get_level_display') # 显示课时,对于一对一,使用表名.字段 hours = serializers.CharField(source='coursedetail.hours') # 显示推荐课程,对于多对多,使用def_字段名 # SerializerMethodField表示序列化字段,使用方法获取 recommend_courses = serializers.SerializerMethodField() # 显示总奖学金,使用表名.字段 total_scholarship = serializers.CharField(source='degree_course.total_scholarship') class Meta: # 配置项 model = models.Course # Course表 fields = ['id','name','level_cn','hours','recommend_courses','total_scholarship'] # 指定序列化的字段 def get_recommend_courses(self,row): recommend_list = row.coursedetail.recommend_courses.all() return [{'id': item.id, 'name': item.name} for item in recommend_list]
刷新页面,效果如下:
contenttypes反向查询
Course表的price_policy字段和PricePolicy表,是用contenttypes关联的
from rest_framework import serializers from api import models class CourseSerializer(serializers.ModelSerializer): # 显示choices里面的中文 level_cn = serializers.CharField(source='get_level_display') # 显示课时,对于一对一,使用表名.字段 hours = serializers.CharField(source='coursedetail.hours') # 显示推荐课程,对于多对多,使用def_字段名 # SerializerMethodField表示序列化字段,使用方法获取 recommend_courses = serializers.SerializerMethodField() # 显示总奖学金,使用表名.字段 total_scholarship = serializers.CharField(source='degree_course.total_scholarship') # 显示价格策略,contenttypes反向查询,使用def_字段名 price_strategy = serializers.SerializerMethodField() class Meta: # 配置项 model = models.Course # Course表 fields = ['id','name','level_cn','hours','recommend_courses','total_scholarship','price_strategy'] # 指定序列化的字段 def get_recommend_courses(self,row): recommend_list = row.coursedetail.recommend_courses.all() return [{'id': item.id, 'name': item.name} for item in recommend_list] def get_price_strategy(self,row): # .all 表示查询关联的价格策略 price_list = row.price_policy.all() return [{'id': item.id, 'valid_period': item.valid_period,'price': item.price} for item in price_list]
重启django项目,刷新页面,效果如下:
总结:
序列化类
一对一,使用 表名小写.字段名
一对多,使用 表名小写.字段名
多对多,使用def_字段名。注意字段必须先声明为serializers.SerializerMethodField()。字段名和def_字段名必须一一对应!
contenttypes反向查询,使用def_字段名。利用contenttypes字段,也是要声明serializers.SerializerMethodField()...
作业
接口
# a.查看所有学位课并打印学位课名称以及授课老师 # b.查看所有学位课并打印学位课名称以及学位课的奖学金 # c.展示所有的专题课 # d. 查看id=1的学位课对应的所有模块名称 # e.获取id = 1的专题课,并打印:课程名、级别(中文)、why_study、what_to_study_brief、所有recommend_courses # f.获取id = 1的专题课,并打印该课程相关的所有常见问题 # g.获取id = 1的专题课,并打印该课程相关的课程大纲 # h.获取id = 1的专题课,并打印该课程相关的所有章节
将上面几个查询改成接口方式,返回json格式。
注意:每一个查询,都是一个独立的接口
vue.js
通过axios向API发送ajax请求,并显示结果(展示所有的专题课)。
注意:会出现跨域问题。请自行百度!提示:搜索关键字cors
参考答案
接口
修改api_urls.py
from django.conf.urls import url from api.views import course,degreecourse urlpatterns = [ url(r'courses/$',course.CoursesView.as_view()), url(r'courses/(?P<pk>\d+)/$',course.CourseDetailView.as_view()), url(r'courses/thematic/$',course.CourseThematicView.as_view()), url(r'courses/module/(?P<pk>\d+)/$',course.CourseModuleView.as_view()), url(r'courses/faq/(?P<pk>\d+)/$',course.CourseFAQView.as_view()), url(r'courses/outline/(?P<pk>\d+)/$',course.CourseOutlineView.as_view()), url(r'courses/chapter/(?P<pk>\d+)/$',course.CourseChapterView.as_view()), url(r'degreecourse/$',degreecourse.DegreeCourseView.as_view()), url(r'degreecourse/teachers/$',degreecourse.DegreeCourseTeachersView.as_view()), url(r'degreecourse/scholarship/$',degreecourse.DegreeCourseScholarshipView.as_view()), ]
修改serializers目录下的course.py
from rest_framework import serializers from api import models class CourseModelSerializer(serializers.ModelSerializer): # 所有课程 level_name = serializers.CharField(source='get_level_display') hours = serializers.CharField(source='coursedetail.hours') course_slogan = serializers.CharField(source='coursedetail.course_slogan') recommend_courses = serializers.SerializerMethodField() class Meta: model = models.Course fields = ['id','name','level_name','hours','course_slogan','recommend_courses'] def get_recommend_courses(self,row): recommend_list = row.coursedetail.recommend_courses.all() return [ {'id':item.id,'name':item.name} for item in recommend_list] class CourseThematicModelSerializer(serializers.ModelSerializer): # 所有的专题课 level_name = serializers.CharField(source='get_level_display') course_type = serializers.CharField(source='get_course_type_display') status = serializers.CharField(source='get_status_display') degree_course = serializers.CharField(source='degree_course.total_scholarship') class Meta: model = models.Course fields = '__all__' class CourseModuleModelSerializer(serializers.ModelSerializer): # 所有的专题课 degree_course = serializers.CharField(source='degree_course.name') class Meta: model = models.Course fields = ['id','degree_course'] class CourseDetailModelSerializer(serializers.ModelSerializer): # 具体id的学位课对应的所有模块名称 level_name = serializers.CharField(source='get_level_display') why_study = serializers.CharField(source='coursedetail.why_study') what_to_study_brief = serializers.CharField(source='coursedetail.what_to_study_brief') recommend_courses = serializers.SerializerMethodField() price_strategy = serializers.SerializerMethodField() class Meta: model = models.Course fields = ['id','name','level_name','why_study','what_to_study_brief','recommend_courses','price_strategy'] def get_recommend_courses(self,row): recommend_list = row.coursedetail.recommend_courses.all() return [ {'id':item.id,'name':item.name} for item in recommend_list] def get_price_strategy(self,row): price_list = row.price_policy.all() return [{'id': item.id, 'valid_period': item.valid_period,'price': item.price} for item in price_list] class CourseFAQModelSerializer(serializers.ModelSerializer): # 具体id专题课程相关的所有常见问题 asked_question = serializers.SerializerMethodField() class Meta: model = models.Course fields = ['id','name', 'asked_question'] def get_asked_question(self, row): faq_list = row.asked_question.all() return [{'id': item.id, 'question': item.question, 'answer': item.answer} for item in faq_list] class CourseOutlineModelSerializer(serializers.ModelSerializer): # 具体id课程相关的课程大纲 asked_question = serializers.SerializerMethodField() class Meta: model = models.Course fields = ['id', 'name', 'asked_question'] def get_asked_question(self, row): outline_list = row.coursedetail.courseoutline_set.all() return [{'id': item.id, 'title': item.title, 'content': item.content} for item in outline_list] class CourseChapterModelSerializer(serializers.ModelSerializer): # 具体id课程相关的所有章节 chapter = serializers.SerializerMethodField() class Meta: model = models.Course fields = ['id', 'name', 'chapter'] def get_chapter(self, row): chapter_list = row.coursechapter_set.all() return [{'id': item.id, 'name': item.name} for item in chapter_list]
修改serializers目录下的degreecourse.py
from rest_framework import serializers from api import models class DegreeCourseModelSerializer(serializers.ModelSerializer): # 学位课所有信息 class Meta: model = models.DegreeCourse fields = '__all__' class DegreeCourseTeachersModelSerializer(serializers.ModelSerializer): # 学位课的老师 teachers = serializers.SerializerMethodField() class Meta: model = models.DegreeCourse fields = ['name','teachers'] def get_teachers(self,row): teachers_list = row.teachers.all() return [ {'id':item.id,'name':item.name} for item in teachers_list] class DegreeCourseScholarshipModelSerializer(serializers.ModelSerializer): # 学位课的奖学金 degreecourse_price_policy = serializers.SerializerMethodField() class Meta: model = models.DegreeCourse fields = ['name','degreecourse_price_policy'] def get_degreecourse_price_policy(self,row): scholarships = row.scholarship_set.all() return [ {'id':item.id,'time_percent':item.time_percent,'value':item.value} for item in scholarships]
在api目录下utils文件夹,创建文件serialization_general.py
from api.utils.response import BaseResponse from rest_framework.pagination import PageNumberPagination class SerializedData(object): # 序列化通用格式数据 def __init__(self,request,queryset,serializer_class): self.request = request self.queryset = queryset self.serializer_class = serializer_class def get_data(self): ret = BaseResponse() try: # 从数据库获取数据 queryset = self.queryset.order_by('id') # 分页 page = PageNumberPagination() course_list = page.paginate_queryset(queryset, self.request, self) # 分页之后的结果执行序列化 ser = self.serializer_class(instance=course_list, many=True) ret.data = ser.data except Exception as e: print(e) ret.code = 500 ret.error = '获取数据失败' return ret.dict
修改views目录下的course.py
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.versioning import URLPathVersioning from rest_framework.pagination import PageNumberPagination from api import models from api.serializers.course import CourseModelSerializer, CourseThematicModelSerializer, CourseModuleModelSerializer, \ CourseDetailModelSerializer,CourseFAQModelSerializer,CourseOutlineModelSerializer,CourseChapterModelSerializer from api.utils.response import BaseResponse from api.utils.serialization_general import SerializedData class CoursesView(APIView): # 所有课程,分页展示,每页1个 def get(self, request, *args, **kwargs): # response = {'code':1000,'data':None,'error':None} queryset = models.Course.objects.all() serializer_class = CourseModelSerializer data = SerializedData(request,queryset,serializer_class).get_data() return Response(data) class CourseDetailView(APIView): # 课程详情 def get(self, request, pk, *args, **kwargs): queryset = models.Course.objects.filter(id=pk) serializer_class = CourseDetailModelSerializer data = SerializedData(request, queryset, serializer_class).get_data() return Response(data) class CourseThematicView(APIView): # 所有的专题课 def get(self, request, *args, **kwargs): queryset = models.Course.objects.all() serializer_class = CourseThematicModelSerializer data = SerializedData(request, queryset, serializer_class).get_data() return Response(data) class CourseModuleView(APIView): # 具体id的学位课对应的所有模块名称 def get(self, request, pk, *args, **kwargs): queryset = models.Course.objects.filter(degree_course_id=pk) serializer_class = CourseModuleModelSerializer data = SerializedData(request, queryset, serializer_class).get_data() return Response(data) class CourseFAQView(APIView): # 具体id的课程相关的所有常见问题 def get(self, request, pk, *args, **kwargs): queryset = models.Course.objects.filter(id=pk) serializer_class = CourseFAQModelSerializer data = SerializedData(request, queryset, serializer_class).get_data() return Response(data) class CourseOutlineView(APIView): # 具体id课程相关的课程大纲 def get(self, request, pk, *args, **kwargs): queryset = models.Course.objects.filter(id=pk) serializer_class = CourseOutlineModelSerializer data = SerializedData(request, queryset, serializer_class).get_data() return Response(data) class CourseChapterView(APIView): # 具体id课程相关的所有章节 def get(self, request, pk, *args, **kwargs): queryset = models.Course.objects.filter(id=pk) serializer_class = CourseChapterModelSerializer data = SerializedData(request, queryset, serializer_class).get_data() return Response(data)
在views目录下创建文件degreecourse.py
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.versioning import URLPathVersioning from rest_framework.pagination import PageNumberPagination from api import models from api.serializers.degreecourse import DegreeCourseModelSerializer,DegreeCourseTeachersModelSerializer from api.serializers.degreecourse import DegreeCourseScholarshipModelSerializer from api.utils.response import BaseResponse class DegreeCourseView(APIView): # 所有学位课 def get(self,request,*args,**kwargs): # response = {'code':1000,'data':None,'error':None} ret = BaseResponse() try: # 从数据库获取数据 queryset = models.DegreeCourse.objects.all() # 分页 page = PageNumberPagination() course_list = page.paginate_queryset(queryset,request,self) # 分页之后的结果执行序列化 ser = DegreeCourseModelSerializer(instance=course_list,many=True) ret.data = ser.data except Exception as e: ret.code = 500 ret.error = '获取数据失败' return Response(ret.dict) class DegreeCourseTeachersView(APIView): # 学位课对应的老师 def get(self, request, *args, **kwargs): ret = BaseResponse() try: # 从数据库获取数据 # 防止出现UnorderedObjectListWarning: Pagination may yield... queryset = models.DegreeCourse.objects.get_queryset().order_by('id') print(queryset) # 分页 page = PageNumberPagination() course_list = page.paginate_queryset(queryset, request, self) # 分页之后的结果执行序列化 ser = DegreeCourseTeachersModelSerializer(instance=course_list, many=True) print(ser.data) ret.data = ser.data except Exception as e: print(e) ret.code = 500 ret.error = '获取数据失败' return Response(ret.dict) class DegreeCourseScholarshipView(APIView): # 学位课对应的老师 def get(self, request, *args, **kwargs): ret = BaseResponse() try: # 从数据库获取数据 # 防止出现UnorderedObjectListWarning: Pagination may yield... queryset = models.DegreeCourse.objects.get_queryset().order_by('id') print(queryset) # 分页 page = PageNumberPagination() course_list = page.paginate_queryset(queryset, request, self) # 分页之后的结果执行序列化 ser = DegreeCourseScholarshipModelSerializer(instance=course_list, many=True) print(ser.data) ret.data = ser.data except Exception as e: print(e) ret.code = 500 ret.error = '获取数据失败' return Response(ret.dict)
访问以下url:
查看所有学位课并打印学位课名称以及授课老师
http://127.0.0.1:8000/api/v1/degreecourse/teachers/
查看所有学位课并打印学位课名称以及学位课的奖学金
http://127.0.0.1:8000/api/v1/degreecourse/scholarship/
展示所有的专题课
http://127.0.0.1:8000/api/v1/courses/thematic/
查看id=1的学位课对应的所有模块名称
http://127.0.0.1:8000/api/v1/courses/module/1/
获取id = 1的专题课,并打印:课程名、级别(中文)、why_study、what_to_study_brief、所有recommend_courses
http://127.0.0.1:8000/api/v1/courses/1/
获取id = 1的专题课,并打印该课程相关的所有常见问题
http://127.0.0.1:8000/api/v1/courses/faq/1/
coursedetail获取id = 1的专题课,并打印该课程相关的课程大纲
http://127.0.0.1:8000/api/v1/courses/outline/1/
获取id = 1的专题课,并打印该课程相关的所有章节
http://127.0.0.1:8000/api/v1/courses/chapter/1/
vue.js
在cmd中进入一个空目录,输入下面的命令,创建一个项目mysite
vue init webpack mysite
执行之后有很多选项,详情请参考:
https://www.cnblogs.com/xiao987334176/p/9372479.html#autoid-5-1-0
执行2个命令,启动vue项目
cd mysite
npm run dev
访问vue的网页:http://localhost:8080
进入vue项目,里面有一个index.html,它是最大的母版。
里面定义了一个div,id为app
进入src目录,修改App.vue。删除图片和css样式
<template> <div id="app"> <router-view/> </div> </template> <script> export default { name: 'App' } </script> <style> </style>
进入src-->components,修改HelloWorld.vue,删除多余的a标签
<template> <div class="hello"> <h1>{{ msg }}</h1> </div> </template> <script> export default { name: 'HelloWorld', data () { return { msg: '欢迎使用路飞学城' } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> h1, h2 { font-weight: normal; } ul { list-style-type: none; padding: 0; } li { display: inline-block; margin: 0 10px; } a { color: #42b983; } </style>
关闭vue项目,安装axios
npm install axios --save
重新启动vue项目
npm run dev
修改main.js,导入axios,并定义一个全局变量$axios
// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' import router from './router' import axios from 'axios' //导入axios Vue.prototype.$axios = axios //声明全局变量 Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
进入src-->components,修改HelloWorld.vue。让页面加载完成后,使用axios发送请求
<template> <div class="hello"> <h1>{{ msg }}</h1> </div> </template> <script> export default { name: 'HelloWorld', data () { return { msg: '欢迎使用路飞学城' } }, mounted(){ //页面加载完成后 this.initCourse(); //执行此方法 }, methods:{ initCourse:function () { //向后台发送ajax请求 this.$axios.request({ url:'http://127.0.0.1:8000/api/v1/courses/', method:'GET', responseType:'json', }).then(function (arg) { //成功之后 console.log(arg); }).catch(function (err) { //发生错误 console.log(err); }) } }, } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> h1, h2 { font-weight: normal; } ul { list-style-type: none; padding: 0; } li { display: inline-block; margin: 0 10px; } a { color: #42b983; } </style>
刷新网页,查看Console,提示不允许访问
只要看到了Access-Control-Allow-Origin,就表示出现跨域了
关于跨域问题,如何解决。请访问下一篇文章:
https://www.cnblogs.com/xiao987334176/articles/9457580.html