剩余组件
"""
分页
搜索
过滤
排序
异常处理
路由
"""
分页
from rest_framework.pagination import PageNumberPagination
# 普通分页
class CarPageNumberPagination(PageNumberPagination):
    # 默认一页的条数
    page_size = 3
    # 用户可以自定义选择一页的条数,但最多显示5条
    page_size_query_param = 'page_size'
    max_page_size = 5
    # 默认条数访问 /cars/?page=页面号
    #       eg:/cars/?page=1
    # 自定义条数访问 /cars/?page=页面号&page_size=一页的条数
    #       eg:/cars/?page=1&page_size=5
from rest_framework.pagination import LimitOffsetPagination
# 偏移分页
class CarLimitOffsetPagination(LimitOffsetPagination):
    # 默认一页的条数
    default_limit = 3
    # limit控制一页显示的条数,offset控制偏移的条数(从头开始计数)
    limit_query_param = 'limit'
    offset_query_param = 'offset'
    # 限制limit可以设置的最大显示条数
    max_limit = 5
    # 接口 /cars/?limit=一页的条数&offset=偏移的条数
    #       eg:/cars/?limit=5&offset=2  # 显示3~7条
from rest_framework.pagination import CursorPagination
# 加密分页
class CarCursorPagination(CursorPagination):
    # 默认一页的条数
    page_size = 3
    # 用户可以自定义选择一页的条数,但最多显示5条
    page_size_query_param = 'page_size'
    max_page_size = 5
    # 默认排序规则
    ordering = 'pk'
    # 采用默认排序访问 /cars/?cursor=加密串
    #       eg:/cars/?cursor=cD0z
    # 结合视图类实现OrderingFilter自定义排序规则
    #  /cars/?cursor=加密串&ordering=排序字段
    #       eg:/cars/?cursor=cD0z&ordering=-price
异常处理
# rest_framework.views 下的 exception_handler 处理了所有 drf可控范围内的异常
from rest_framework.views import exception_handler as drf_exception_handler
# drf的异常还是交给 drf_exception_handler,我们只需要处理 drf未处理的异常
from rest_framework.response import Response
# 自定义异常句柄的原因:要通过 logging 记录异常日志
def exception_handler(exc, context):
    response = drf_exception_handler(exc, context)
    if response is None:
        # drf处理不了的异常
        return Response({
            'exception': '服务器异常',
        }, status=500)
    response.exception = True
    return response
过滤
from django_filters.rest_framework import FilterSet
from django_filters import filters
from . import models
class CarFilterSet(FilterSet):
    min_price = filters.NumberFilter(field_name='price', lookup_expr='gte')
    max_price = filters.NumberFilter(field_name='price', lookup_expr='lte')
    class Meta:
        model = models.Car
        # brand还是实现分类
        # min_price,max_price可以定义区间
        fields = ['brand', 'min_price', 'max_price']
序列化
from rest_framework.serializers import ModelSerializer
from . import models
class CarModelSerializer(ModelSerializer):
    class Meta:
        model = models.Car
        fields = ('name', 'price', 'brand', 'brand_name')
        extra_kwargs = {
            'brand': {
                'write_only': True
            },
            'brand_name': {
                'read_only': True
            },
        }
        # list_serializer_class = TempListSerializer
路由
from django.conf.urls import url
from . import views
from rest_framework.routers import SimpleRouter
# 初始化路由对象
router = SimpleRouter()
# 注册各种接口路由
router.register('cars', views.CarModelViewSet, base_name='car')
# router.register('brands', views.BrandModelViewSet, base_name='brand')
urlpatterns = [
    # url(r'^cars/$', views.CarModelViewSet.as_view({
    #     'get': 'list',
    #     'post': 'create'
    # })),
    # url(r'^cars/(?P<pk>.*)/$', views.CarModelViewSet.as_view({
    #     'get': 'retrieve',
    #     'put': 'update',
    #     'patch': 'partial_update',
    #     'delete': 'destroy'
    # })),
]
urlpatterns.extend(router.urls)
搜索、排序
from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response
from . import models, serializers
from rest_framework.filters import SearchFilter, OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
from .filtersets import CarFilterSet
from . import pagination
class CarModelViewSet(ModelViewSet):
    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializers.CarModelSerializer
    def destroy(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        try:
            car_obj = models.Car.objects.get(pk=pk, is_delete=False)
            car_obj.is_delete = True
            car_obj.save()
            return Response({
                'status': 0,
                'msg': '删除成功'
            })
        except:
            return Response({
                'status': 1,
                'msg': '删除失败'
            })
    def list(self, request, *args, **kwargs):
        response = super().list(request, *args, **kwargs)
        return Response({
            'status': 0,
            'msg': 'ok',
            'results': response.data
        })
    # 搜索、排序、筛选(分类)、分页 => list 的 filter 们
    # 搜索组件
    # 接口:/cars/?search=秦  ||  /cars/?search=1  在name和price两个字段中模糊搜索
    # filter_backends = [SearchFilter]
    # 默认模糊查询,等价$,以什么开头^,精确匹配=,@??
    search_fields = ['name', 'price']
    # 排序组件
    # filter_backends = [SearchFilter, OrderingFilter]
    ordering_fields = ['pk', 'price']
    # 筛选(分类、区间)
    filter_backends = [SearchFilter, OrderingFilter, DjangoFilterBackend]
    # 分类:一般都是可以分组的字段
    # filter_fields = ['brand']
    # 区间,也可以包含分类,提倡 筛选 就采用该方式
    filter_class = CarFilterSet
    # 分页
    # 普通分页(最常用)
    # pagination_class = pagination.CarPageNumberPagination
    # 偏移分页
    # pagination_class = pagination.CarLimitOffsetPagination
    # 加密分页
    pagination_class = pagination.CarCursorPagination
from django.db import models
class BaseModel(models.Model):
    is_delete = models.BooleanField(default=0)
    create_time = models.DateTimeField(auto_now_add=True)
    class Meta:
        abstract = True
class Car(BaseModel):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    brand = models.ForeignKey('Brand', db_constraint=False, on_delete=models.DO_NOTHING, related_name='cars')
    @property
    def brand_name(self):
        return self.brand.name
    class Meta:
        db_table = 'old_boy_car'
        verbose_name = '汽车'
        verbose_name_plural = verbose_name
    def __str__(self):
        return self.name
class Brand(BaseModel):
    name = models.CharField(max_length=16)
    class Meta:
        db_table = 'old_boy_brand'
        verbose_name = '品牌'
        verbose_name_plural = verbose_name
    def __str__(self):
        return self.name