一.准备工作:
1.主路由:
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^api/', include('api.urls')), ]
2.api路由:
from django.conf.urls import url
from . import views
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' })), ]
3.setting.py:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'api.apps.ApiConfig', 'rest_framework', ]
4.models:
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
5.视图类:
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
})
二.路由组件 (写前面信息'cars'即可,有名分组自动补全pk):
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 = [ ] urlpatterns.extend(router.urls)
三.搜索组件
主要是利用mixins.ListModelMixin类找到queryset,进而进行搜索,过滤,排序等操作
在filters.py文件中有SearchFilter和OrderingFilter两个类:
class SearchFilter(BaseFilterBackend): # The URL query parameter used for the search. search_param = api_settings.SEARCH_PARAM 默认是search template = 'rest_framework/filters/search.html' lookup_prefixes = { '^': 'istartswith', '=': 'iexact', '@': 'search', '$': 'iregex', } search_title = _('Search') search_description = _('A search term.')
需要两个参数:
def filter_queryset(self, request, queryset, view): search_fields = self.get_search_fields(view, request) search_terms = self.get_search_terms(request)
视图类中自定义写法:
from rest_framework.filters import SearchFilter, OrderingFilter
# 搜索组件 # 接口:/cars/?search=秦 || /cars/?search=1 在name和price两个字段中模糊搜索 filter_backends = [SearchFilter] # 默认模糊查询,等价$,以什么开头^,精确匹配=,@?? search_fields = ['name', 'price']
四.排序组件(支持倒序)
filter_backends = [SearchFilter, OrderingFilter] ordering_fields = ['pk', 'price']
# 接口: api/cars/?search=1&ordering=-pk
五.筛选组件
安装:
pip install django-filter
配置:
INSTALLED_APPS = [ ... 'django_filters', # 需要注册应用, ]
全局(也可以设置局部): REST_FRAMEWORK = { ... 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) }
视图类中:
from .filtersets import CarFilterSet
# 筛选(分类、区间) filter_backends = [SearchFilter, OrderingFilter, DjangoFilterBackend] # 分类:一般都是可以分组的字段 # filter_fields = ['brand'] # 区间,也可以包含分类,提倡 筛选 就采用该方式 filter_class = CarFilterSet
区间类:
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']
六.分页组件
视图分页:
# 分页 # 普通分页(最常用) # pagination_class = pagination.CarPageNumberPagination # 偏移分页 # pagination_class = pagination.CarLimitOffsetPagination # 加密分页 pagination_class = pagination.CarCursorPagination
自定义pagination.py:
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
七.异常处理
配置:
# drf配置 REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'api.exception.exception_handler' }
自定义exception:
# 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
八.接口文档(yapi)