课程主页之课程接口
一、课程分类接口
1. course/views.py
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from . import serializer
from . import models
# 查询所有课程分类接口
class CourseCategoryView(GenericViewSet, ListModelMixin):
queryset = models.CourseCategory.objects.filter(is_delete=False, is_show=True).order_by('orders').all()
serializer_class = serializer.CourseCategorySerializer
2. course/serializer.py
from rest_framework import serializers
from . import models
# 课程分类序列化器
class CourseCategorySerializer(serializers.ModelSerializer):
class Meta:
model = models.CourseCategory
fields = ['id', 'name', ]
3. course/urls.py
from django.urls import path, include
from . import views
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('categories', views.CourseCategoryView, 'categories')
urlpatterns = [
path('', include(router.urls)),
]
4. apps/urls.py
path('course/', include('course.urls')),
5. 流程
1. 视图定义课程分类视图, 继承GenericViewSet, ListModelMixin
2. 对课程分类接口进行is_delete, is_show过滤, 再使用orders进行排序
3. 序列化定义课程分类序列化类. 不过需要给课程分类的id, 在分类的时候, 需要前端传递过来实现分类. 然后只需要给课程分类名即可.
4. 配置course中的路由, 配置总路由
二、课程接口
1. views.py
# 查询所有课程详情接口
from .paginations import PageNumberPagination
from rest_framework.filters import OrderingFilter, SearchFilter
from django_filters.rest_framework import DjangoFilterBackend
# 自定义的过滤规则
from .filters import Myfilter
# 区间过滤
from .filters import CourseFilterSet
# 单查、群查课程接口
class CourseView(GenericViewSet, ListModelMixin, RetrieveModelMixin):
queryset = models.Course.objects.filter(is_delete=False, is_show=True).order_by('orders').all()
serializer_class = serializer.CourseSerializer
# 分页
pagination_class = PageNumberPagination
''' 原始版本的过滤+排序 '''
# 排序
# filter_backends = (OrderingFilter, DjangoFilterBackend, SearchFilter)
filter_backends = (OrderingFilter, DjangoFilterBackend)
ordering_fields = ['id', 'price', 'students']
'''
过滤+排序分析:
1.先走执行ListModelMixin中的List方法
2.List方法中回执行self.filter_queryset方法,但是ListModelMixin中没有filter_queryset方法
3.从GenericViewSet类中找filter_queryset方法,也没有
4.GenericViewSet类继承了ViewSetMixin, generics.GenericAPIView,ViewSetMixin中也没有,在generics.GenericAPIView类中找到了filter_queryset方法
5.generics.GenericAPIView中的filter_queryset代码如下
for backend in list(self.filter_backends): 遍历CourseView中的filter_backends
queryset = backend().filter_queryset(self.request, queryset, self) :实例化filter_backends列表中的类的,并执行filter_queryset方法
return queryset:返回queryset对象
'''
# 过滤(django内置的过滤)
# search_field = ['id']
# 过滤(django-filter提供的过滤,比内置的更加强大,可以过滤外键字段)
filter_fields = ['course_category', ]
'''
排序:
使用:http://www127.0.0.1:8000/course/free/?ordering=-id
按照id正序倒序排序
配置类:
filter_backends = (OrderingFilter,)
配置字段:
ordering_fields = ['id',]
内置过滤:
使用:http://www127.0.0.1:8000/course/free/?search=39.00
按照price过滤(过滤表中自有的字段直接过滤)
配置类:
filter_backends = (SearchFilter,)
配置字段:
search_field = ['price']
扩展过滤(django-filter)
使用:http://www127.0.0.1:8000/course/free/?course_category=1
按照course_category过滤(无论是过滤表中自有的字段还是外键字段)
配置类:
filter_backends = (DjangoFilterBackend)
配置字段:
filter_fields = ['course_category', ]
'''
''' django-filter版本的过滤 '''
# filter_backends = (DjangoFilterBackend,)
# filter_class = CourseFilterSet
2. serializer.py
class TeacherModelSerializer(serializers.ModelSerializer):
class Meta:
model = Teacher
fields = ('name', 'role_name', 'title', 'signature', 'image', 'brief')
class CourseModelSerializer(serializers.ModelSerializer):
# teacher子序列化
teacher = TeacherModelSerializer()
class Meta:
model = Course
fields = (
'id',
'name',
'course_img',
'brief',
'attachment_path',
'pub_sections',
'price',
'students',
'period',
'sections',
'course_type_name',
'level_name',
'status_name',
'teacher',
'section_list',
)
3. models.py
class Course(BaseModel):
...
@property
def level_name(self):
return self.get_level_display()
@property
def course_type_name(self):
return self.get_course_type_display()
@property
def status_name(self):
return self.get_status_display()
@property
def section_list(self):
course_chapter_queryset = self.coursechapters.all()
course_section_list = []
for course_chapter in course_chapter_queryset:
course_section_queryset = course_chapter.coursesections.all()
for course_section in course_section_queryset:
course_section_list.append({
'name': course_section.name,
'section_type': course_section.section_type,
'section_link': course_section.section_link,
'duration': course_section.duration,
})
if len(course_section_list) == 4:
return course_section_list
return course_section_list
class Teacher(BaseModel):
...
@property
def role_name(self):
return self.get_role_display()
from rest_framework.pagination import PageNumberPagination as DRFPageNumberPagination
class PageNumberPagination(DRFPageNumberPagination):
page_size = 1
page_query_param = 'page'
page_size_query_param = 'page_size'
max_page_size = 10
5. filters.py自定义过滤类
from rest_framework.filters import BaseFilterBackend
class CustomFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
# 老师的模糊匹配
name = request.GET.get('teacher')
if not name:
return queryset
teacher_queryset = queryset.filter(teacher__name__contains=name)
return teacher_queryset
6. urls.py
router.register('free', views.CourseView, 'free')
7. 流程
1. 视图中定义课程视图类继承GenericViewSet,ListModelMixin
2. 视图中queryset的查询过滤is_delete, is_show, 再使用orders字段进行排序
3. 序列化中定义课程序列化类, 控制序列化的字段, 在序列化类中针对teacher使用子序列化进行控制teacher包含字段对应数值的展示
4. 对应choices字段的展示, 有2种方式进行控制, 在序列化类中重写字段, 使用serializerMethod, 模型类中使用@property装饰返回对应get_字段_display()字段参数对应的值.
5. 配置路由
6. 配置分页器. 自定义分页器, 使用了as的特殊用法
三、排序和过滤组件
1. 内置排序组件OrderingFIlter
# 1)在视图文件views.py中导入drf的搜索组件
from rest_framework.filters import OrderingFilter
# 2)将搜索组件配置给群查接口视图类的filter_backends
filter_backends = [OrderingFilter]
# 3)配置视图类关联的Model表允许排序的字段
ordering_fields = ['id', 'price']
# 4)前台访问该群查接口,采用拼接参数方式用search关键字将搜索目标提供给后台
http://127.0.0.1:8000/course/free/?ordering=price,-id # 按price升序,如果price相同,再按id降序
2. 过滤组件
<1>. 内置过滤组件SearchFIlter
# 缺点: 外键字段的搜索操作将会抛出异常: Related Field got invalid lookup: icontains
# 1)在视图文件views.py中导入drf的搜索组件
from rest_framework.filters import SearchFilter
# 2)将搜索组件配置给群查接口视图类的filter_backends
filter_backends = [SearchFilter]
# 3)配置视图类关联的Model表参与搜索的字段
search_fields = ['name', 'id']
# 4)前台访问该群查接口,采用拼接参数方式用search关键字将搜索目标提供给后台
http://127.0.0.1:8000/course/free/?search=2 # id或name中包含2的所有结果
<2>. 第三方过滤组件django-filter
# 介绍: 争对django内置搜索组件的拓展, 在django内置的基础之上还拓展了外键字段的过滤功能.
# 前提:安装django-filter插件
pip install django-filter (注意: 不要安装成了django-filters)
"""方式一"""
# 1)在视图文件views.py中导入django-filter的功能组件
from django_filters.rest_framework import DjangoFilterBackend
# 2)将搜索组件配置给群查接口视图类的filter_backends
filter_backends = [DjangoFilterBackend]
# 3)配置视图类关联的Model表可以分类的字段(通常是可以分组的字段)
filter_fields = ['course_category']
# 4)前台访问该群查接口,采用拼接参数方式用分类course_category字段将分类条件提供给后台
http://127.0.0.1:8000/course/free/?course_category=1 # 拿课程分类1下的所有课程
'''方式二'''
# 1)自定义过滤类继承django-filter插件的FilterSet类,绑定Model表,并设置分类字段
from django_filters.filterset import FilterSet
from . import models
class CourseFilterSet(FilterSet):
class Meta:
model = models.Course
fields = ['course_category']
# 2)在视图文件views.py中导入django-filter的功能组件及自定义的过滤类
from django_filters.rest_framework import DjangoFilterBackend
from .filters import CourseFilterSet
# 3)将搜索组件配置给群查接口视图类的filter_backends
filter_backends = [DjangoFilterBackend]
# 4)配置视图类关联的自定义过滤类
filter_class = CourseFilterSet
# 5)前台访问该群查接口,采用拼接参数方式用分类course_category字段将分类条件提供给后台
http://127.0.0.1:8000/course/free/?course_category=1 # 拿课程分类1下的所有课程
<3>. django-filter实现区间过滤
# 1)自定义过滤类继承django-filter插件的FilterSet类,绑定Model表,并设置自定义区间规则字段
from django_filters.filterset import FilterSet
from . import models
class CourseFilterSet(FilterSet):
# 区间过滤: students学生中总人数要大于等于min_students, 要小于等于max_students. [min_students, max_students]
max_students = filters.NumberFilter(field_name='students', lookup_expr='lte')
min_students = filters.NumberFilter(field_name='students', lookup_expr='gte')
class Meta:
model = Course
fields = ['course_category', 'students', 'min_students', 'max_students']
# 2)在视图文件views.py中导入django-filter的功能组件及自定义的过滤类
from django_filters.rest_framework import DjangoFilterBackend
from .filters import CourseFilterSet
# 3)将搜索组件配置给群查接口视图类的filter_backends
filter_backends = [DjangoFilterBackend]
# 4)配置视图类关联的自定义过滤类
filter_class = CourseFilterSet
# 5)前台访问该群查接口,采用拼接参数方式用自定义区间规则字段将区间条件提供给后台
http://127.0.0.1:8000/course/free/?min_students=230&max_students=250 # 获取学生总人数230~250之间的数据
'''
# django-filter区间过滤源码流程
关键: filter_queryset
get_filterset_class:
反射filter_class
MetaBase
AutoFilterSet
filterset就是我们自定义类实例化出来的对象
有值调用: filterset.is_valid()
reutrn filterset.qs qs在BaseFilterSet中
return self.qs = qs queryset对象
反射filter_fields
'''
<4>. 自定义过滤
# filters.py
from rest_framework.filters import BaseFilterBackend
class CustomFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
# 老师的模糊匹配
name = request.GET.get('teacher')
if not name:
return queryset
teacher_queryset = queryset.filter(teacher__name__contains=name)
return teacher_queryset
# 此时返回的queryset就是过滤好的数据
# views.py
# 自定义过滤: 通过老师名进行模糊匹配
filter_backends = [CustomFilter]
四、总结
# 过滤, 排序, 分页的使用范围
视图类继承关系中必须含有的视图类: ListModelMixin, GenericAPIView
ListModelMixin中实现的是调用GenericAPIView中对应的过滤,排序(filter_queryset), 分页(paginate_queryset)方法
GenericAPIView中实现的是循环调用视图类中配置的类, 调用视图类中对应的方法
def filter_queryset(self, queryset):
for backend in list(self.filter_backends):
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset