GenericAPIView 通用视图类
get_serializer_class(self) 获取序列化类
当出现一个视图类中调用多个序列化器时,那么可以通过条件判断在get_serializer_class方法中通过不同的序列化器类名就可以让视图方法执行不同的序列化器对象了。
get_serializer(self) 获取序列化对象
返回序列化器对象,主要用来提供给Mixin扩展类使用,我们如果在视图中想要获取序列化器对象,也可以使用这方法。
get_queryset(self)
返回视图使用的查询集。
get_object(self)
返回详情视图所需的模型数据对象
案例
view
from book.models import Book
from rest_framework import serializers
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
# 模型序列化器
class BookModelSerializers(serializers.ModelSerializer):
# pub_date 转化为 date
date = serializers.DateField(source="pub_date")
class Meta:
model = Book
# fields = '__all__'
exclude = ['pub_date']
# View
class BookGenericAPIView(GenericAPIView):
# 我们的数据
queryset = Book.objects.all()
# 我们的序列化器
serializer_class = BookModelSerializers
def get(self, request):
# 这样可以实现功能,但是不好看
# serializer = self.get_serializer_class()(instance=self.get_queryset(), many=True)
# 这样可以直接实现,他帮你实现了
serializer = self.get_serializer(instance=self.get_queryset(), many=True)
return Response(serializer.data)
def post(self, request):
serializer = self.get_serializer(data=request.data)
if not serializer.is_valid():
return Response(serializer.errors)
serializer.save()
return Response(serializer.data)
# 通过id拿数据的View
class BookDetailGenericAPIView(GenericAPIView):
# 我们的数据
queryset = Book.objects.all()
# 我们的序列化器
serializer_class = BookModelSerializers
def get(self, request, pk):
# 这样可以实现功能,但是不好看
# book = Book.objects.get(pk=id)
# serializer = self.get_serializer(instance=book)
serializer = self.get_serializer(instance=self.get_object())
return Response(serializer.data)
def put(self, request, pk):
serializer = self.get_serializer(instance=self.get_object(), data=request.data)
if not serializer.is_valid():
return Response(serializer.errors)
serializer.save()
return Response(serializer.data)
def delete(self, request, pk):
self.get_object().delete()
return Response()
url
from django.urls import path, re_path
from . import views
urlpatterns = [
path('book/view/', views.BookGenericAPIView.as_view()),
# (?P<pk>\d+)这个位置传来的数据是有名传参 参数名是pk
re_path('book/view/(?P<pk>\d+)', views.BookDetailGenericAPIView.as_view()),
# path('book/view/<int:pk>', views.BookDetailGenericAPIView.as_view()),
]
Mixin混合类
ListModelMixin
源码
class ListModelMixin:
"""
List a queryset.
"""
# 类中有个list方法
def list(self, request, *args, **kwargs):
# 获取我们的查询并做过滤
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)
案例
更具上面的源码我们的代码可以这样写
# 列表混合类中没有继承任何父类,所以我们这里还需要去继承一下通用的AIPView
class BookMixinView(ListModelMixin, GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookModelSerializers
def get(self, request):
# 这就是listModelMixin中的list方法直接返回即可
return self.list(request)
CreateModelMixin
同 ListModelMixin 一样
class BookMixinView(CreateModelMixin, GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookModelSerializers
def post(self, request):
return self.create(request)
UpdateModelMixin
同 ListModelMixin 一样
class BookDetailMixinView(RetrieveModelMixin, GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookModelSerializers
def get(self, request, pk):
return self.retrieve(request, pk)
RetrieveModelMixin
同 ListModelMixin 一样
class BookDetailMixinView(UpdateModelMixin, GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookModelSerializers
def put(self, request, pk):
return self.update(request, pk)
DestroyModelMixin
同 ListModelMixin 一样
class BookDetailMixinView(DestroyModelMixin, GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookModelSerializers
def delete(self, request, pk):
return self.destroy(request, pk)
Mixin混合类再封装
ListCreateAPIView
源码
该类继承了List和Create混合类以及通用APIView,并且替我们创建了get和post方法
class ListCreateAPIView(mixins.ListModelMixin,
mixins.CreateModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset or creating a model instance.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
案例
所以!我们代码就成了这样
from book.models import Book
from rest_framework.response import Response
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
class BookMixinAPIView(ListCreateAPIView):
queryset = Book.objects.all()
serializer_class = BookModelSerializers
RetrieveUpdateDestroyAPIView
同 ListCreateAPIView
class BookDetailMixinAPIView(RetrieveUpdateDestroyAPIView):
queryset = Book.objects.all()
serializer_class = BookModelSerializers
ViewSet 五合一
ViewSet 重新构建了分发机制,我们可以再路由级别as_view()添加映射
from django.urls import path, include, re_path
from . import views
urlpatterns = [
path('book/mixin/view_set/', views.BookViewSet.as_view({'get': 'get_all'})),
re_path('book/mixin/view_set/(?P<pk>\d+)', views.BookViewSet.as_view({'get': 'get_object'})),
]
如上所示,我们将get请求映射到不同的函数上,而view只需要继承和实现对应的函数
from book.models import Book
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet
class BookViewSet(ViewSet):
def get_all(self, request):
return Response("get_all")
def post(self, request):
return Response("post")
def get_object(self, request, pk):
return Response("get_object" + str(pk))
def put(self, request, pk):
return Response("get_all" + str(pk))
def delete(self, request, pk):
return Response("delete" + str(pk))
如何实现的五合一?源码
代码太多,跳过吧,看重点
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
"""
Because of the way class based views create a closure around the
instantiated view, we need to totally reimplement `.as_view`,
and slightly modify the view function that is created and returned.
"""
# The name and description initkwargs may be explicitly overridden for
# certain route configurations. eg, names of extra actions.
cls.name = None
cls.description = None
# The suffix initkwarg is reserved for displaying the viewset type.
# This initkwarg should have no effect if the name is provided.
# eg. 'List' or 'Instance'.
cls.suffix = None
# The detail initkwarg is reserved for introspecting the viewset type.
cls.detail = None
# Setting a basename allows a view to reverse its action urls. This
# value is provided by the router through the initkwargs.
cls.basename = None
# actions must not be empty
if not actions:
raise TypeError("The `actions` argument must be provided when "
"calling `.as_view()` on a ViewSet. For example "
"`.as_view({'get': 'list'})`")
# sanitize keyword arguments
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r" % (
cls.__name__, key))
# name and suffix are mutually exclusive
if 'name' in initkwargs and 'suffix' in initkwargs:
raise TypeError("%s() received both `name` and `suffix`, which are "
"mutually exclusive arguments." % (cls.__name__))
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if 'get' in actions and 'head' not in actions:
actions['head'] = actions['get']
# We also store the mapping of request methods to actions,
# so that we can later set the action attribute.
# eg. `self.action = 'list'` on an incoming GET request.
self.action_map = actions
# Bind methods to actions
# This is the bit that's different to a standard view
for method, action in actions.items():
handler = getattr(self, action)
setattr(self, method, handler)
self.request = request
self.args = args
self.kwargs = kwargs
# And continue as usual
return self.dispatch(request, *args, **kwargs)
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
# We need to set these on the view function, so that breadcrumb
# generation can pick out these bits of information from a
# resolved URL.
view.cls = cls
view.initkwargs = initkwargs
view.actions = actions
return csrf_exempt(view)
重点在这里
在 ViewSetMixin.as_view()
# 重写as_view,让我们可以传一个actions 我们传入的就是{'get': 'get_all'}
def as_view(cls, actions=None, **initkwargs):
...
# 这里 method 就是 get 而action 就是get_all
for method, action in actions.items():
# 这边去调用指定的方法 也就是找 get_all方法
handler = getattr(self, action)
# 去将method设置成对应的方法 也就是给 get 设置成 get_all
setattr(self, method, handler)
...
而在 rest_framework.views.APIView.dispatch 中 有如下一段代码 其中
getattr(self, request.method.lower(),self.http_method_not_allowed)
这里拿到的就是我们上面定义的get_all
源码如下
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
最终版,究极封装!ModelViewSet 和 路由组件
view
from book.models import Book
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
class BookModelSerializers(serializers.ModelSerializer):
# pub_date 转化为 date
date = serializers.DateField(source="pub_date")
class Meta:
model = Book
# fields = '__all__'
exclude = ['pub_date']
class BookViewSet(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookModelSerializers
url
from rest_framework import routers
router = routers.DefaultRouter()
router.register('book/model/view/set', views.BookViewSet, basename="book")
urlpatterns = []
urlpatterns += router.urls