零、视图家族

Django REST framework 为了方便视图类的操作,构建了包括以下几种视图类和工具集:

  • views:API视图

  • generics:工具视图

  • mixins:视图工具集

  • viewsets:视图集

一、GenericAPIView:视图家族的基类

generics.py 中的 GenericAPIView 作为视图家族中重要的基类,在后面的接口代码的实现中起到重要作用。

用 Pycharm 可以看到 GenericAPIView 的方法和继承关系:
mark

GenericAPIView 继承自 APIView,使用兼容 APIView,也就是说依旧可以使用 get,post等方法。

但是,这里 GenericAPIView 封装了更多有趣的方法。

  • get_queryset(): 从类属性 queryset 中获得 model 的 queryset 数据
  • get_object(): 通过有名分组 pk 确定唯一操作对象
    自定义主键的有名分组 :lookup_field = ‘id’
  • get_serializer():从类属性 serializer_class 中获得serializer 的序列化类

二、Mixins:视图工具集

mixins.py:视图工具集,用来辅助 GenericAPIView
mark

包含有五个工具类文件,六个工具类方法:

  • CreateModeMixin:
    • 单增:create
  • ListModelMixin:
    • 群查:list
  • RetrieveModelMixin:
    • 单查:retrieve
  • UpdateModelMixin:
    • 单整体改:update
    • 单局部改:partial_update
  • DestroyModelMixin:
    • 单删:destroy,一般不使用该方法,或重写方法利用字段is_delete 来实现删除操作。

1.群查

查看源码

mark

class ListModelMixin:
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
# get_queryset 通过子类继承 GenericAPIView 得到
queryset = self.filter_queryset(self.get_queryset())

page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)

serializer = self.get_serializer(queryset, many=True)
class ListModelMixin:
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
# get_queryset 通过子类继承 GenericAPIView 得到
queryset = self.filter_queryset(self.get_queryset())

page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)

serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
代码实现

继承工具类可以简化请求函数的实现体,但是必须继承 GenericAPIView,因为需要 GenericAPIView 提供类属性和方法。

工具类方法返回值是 Response 对象,可以用 response.data 拿到,扔给之前封装的APIResponse 类实现格式。

views.py

class BookListGenericAPIView(ListModelMixin, GenericAPIView):
queryset = models.Book.objects.filter(is_delete=False)
serializer_class = serializers.BookModelSerializer

def get(self, request, *args, **kwargs):
# 调用 ListModelMixin 的 list方法
response = self.list(request, *args, **kwargs)
# 添加自己封装的 APIResponse
class BookListGenericAPIView(ListModelMixin, GenericAPIView):
queryset = models.Book.objects.filter(is_delete=False)
serializer_class = serializers.BookModelSerializer

def get(self, request, *args, **kwargs):
# 调用 ListModelMixin 的 list方法
response = self.list(request, *args, **kwargs)
# 添加自己封装的 APIResponse
return APIResponse(results=response.data)

urls.py

from django.conf.urls import url

from api import views

urlpatterns = [
url(r'^v3/books/$', views.BookListGenericAPIView.as_view()),
url(from django.conf.urls import url

from api import views

urlpatterns = [
url(r'^v3/books/$', views.BookListGenericAPIView.as_view()),
url(r'^v3/books/(?P.*)/$', views.BookListGenericAPIView.as_view()),
]

测试接口

mark

2.单查

查看源码

mark

class RetrieveModelMixin:
"""
Retrieve a model instance.
"""
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
class RetrieveModelMixin:
"""
Retrieve a model instance.
"""
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
代码实现
class BookListGenericAPIView(ListModelMixin, RetrieveModelMixin, GenericAPIView):
queryset = models.Book.objects.filter(is_delete=False)
serializer_class = serializers.BookModelSerializer

def get(self, request, *args, **kwargs):
if 'pk' in kwargs:
response = self.retrieve(request, *args, **kwargs)
else:
response = self.list(request, *args, **kwargs)
class BookListGenericAPIView(ListModelMixin, RetrieveModelMixin, GenericAPIView):
queryset = models.Book.objects.filter(is_delete=False)
serializer_class = serializers.BookModelSerializer

def get(self, request, *args, **kwargs):
if 'pk' in kwargs:
response = self.retrieve(request, *args, **kwargs)
else:
response = self.list(request, *args, **kwargs)
return APIResponse(results=response.data)
测试接口

mark

3.单增

查看源码

mark

查看 CreateModelMixin 源码

class CreateModelMixin:
"""
Create a model instance.
"""
def create(self, request, *args, **kwargs):
# 序列化
serializer = self.get_serializer(data=request.data)
# 验证
serializer.is_valid(raise_exception=True)
# 表单数据重建,函数名有语义,而直接调用逻辑是无意义的
self.perform_create(serializer)
# 设置 header
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

def perform_create(self, serializer):
serializer.save()

def get_success_headers(self, data):
try:
return {'Location': str(data[api_settings.URL_FIELD_NAME])}
except (TypeError, KeyError):
class CreateModelMixin:
"""
Create a model instance.
"""
def create(self, request, *args, **kwargs):
# 序列化
serializer = self.get_serializer(data=request.data)
# 验证
serializer.is_valid(raise_exception=True)
# 表单数据重建,函数名有语义,而直接调用逻辑是无意义的
self.perform_create(serializer)
# 设置 header
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

def perform_create(self, serializer):
serializer.save()

def get_success_headers(self, data):
try:
return {'Location': str(data[api_settings.URL_FIELD_NAME])}
except (TypeError, KeyError):
return {}
代码实现

可以看到这个类就是把之前单增的方法封装在一起方便调用

class BookListGenericAPIView(ListModelMixin, CreateModelMixin, GenericAPIView):
queryset = models.Book.objects.filter(is_delete=False)
serializer_class = serializers.BookModelSerializer

def post(self, request, *args, **kwargs):
response = self.create(request, *args, **kwargs)
class BookListGenericAPIView(ListModelMixin, CreateModelMixin, GenericAPIView):
queryset = models.Book.objects.filter(is_delete=False)
serializer_class = serializers.BookModelSerializer

def post(self, request, *args, **kwargs):
response = self.create(request, *args, **kwargs)
return APIResponse(results=response.data)
测试接口

mark

4.单改

查看源码
class UpdateModelMixin:
"""
Update a model instance.
"""
# 实现单整体改
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)

if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}

return Response(serializer.data)

def perform_update(self, serializer):
serializer.save()

# 单局部改,调用 update 设置 partial = True
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
class UpdateModelMixin:
"""
Update a model instance.
"""
# 实现单整体改
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)

if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}

return Response(serializer.data)

def perform_update(self, serializer):
serializer.save()

# 单局部改,调用 update 设置 partial = True
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
代码实现
class BookListGenericAPIView(ListModelMixin, UpdateModelMixin, GenericAPIView):
queryset = models.Book.objects.filter(is_delete=False)
serializer_class = serializers.BookModelSerializer

def put(self, request, *args, **kwargs):
response = self.update(request, *args, **kwargs)
return APIResponse(results=response.data)

def patch(self, request, *args, **kwargs):
response = self.partial_update(request, *args, **kwargs)
class BookListGenericAPIView(ListModelMixin, UpdateModelMixin, GenericAPIView):
queryset = models.Book.objects.filter(is_delete=False)
serializer_class = serializers.BookModelSerializer

def put(self, request, *args, **kwargs):
response = self.update(request, *args, **kwargs)
return APIResponse(results=response.data)

def patch(self, request, *args, **kwargs):
response = self.partial_update(request, *args, **kwargs)
return APIResponse(results=response.data)
测试接口

mark

修改成功mark