REST framework 提供了一个 APIView
类,它继承于 Django 的 View
类。
APIView
类与不同的 View
类有所不同:
- 传递给处理方法的 request 对象是 REST framework 的
Request
实例,而不是 Django 的HttpRequest
实例。 - 处理方法可能返回 REST framework 的
Response
,而不是 Django 的HttpResponse
。该视图将管理内容协商,并在响应中设置正确的渲染器。 - 任何
APIException
异常都会被捕获并进行适当的响应。 - 传入的请求会进行认证,在请求分派给处理方法之前将进行适当的权限检查(允许或限制)。
像往常一样,使用 APIView
类与使用常规 View
类非常相似,传入的请求被分派到适当的处理方法,如 .get()
或.post()
。此外,可以在类上设置许多属性(AOP)。
举个例子,我们要实现Authorl类的增删改查视图:
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name=models.CharField( max_length=32)
age=models.IntegerField()
# 与AuthorDetail建立一对一的关系
authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE)
def __str__(self):
return self.name
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^author/$',views.AuthorView.as_view()),
url(r'^author/(?P<pk>\d+)/$',views.AuthorDetailView.as_view()),
]
from rest_framework import serializers
from app01.models import *
class AuthorSerializers(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__"
(1) 我们基于APIView来实现Author类的增删改查
from app01.models import *
from rest_framework.views import APIView
from django.core.serializers import serialize
from rest_framework.response import Response
from app01.serializers import AuthorSerializers
class AuthorView(APIView):
# 查看所有的数据接口
def get(self, request):
model_obj_list = Author.objects.all()
# django的序列化组件
# ret=serialize('json',publish_list)
# return HttpResponse(ret)
# REST的序列化组件
bs = AuthorSerializers(model_obj_list, many=True)
return Response(bs.data)
# 增加提交一个数据
def post(self, request):
bs = AuthorSerializers(data=request.data)
if bs.is_valid():
bs.save()
return Response(bs.data)
else:
return Response(bs.errors)
class AuthorDetailView(APIView):
# 查看单个数据
def get(self, request, pk):
obj = Author.objects.filter(pk=pk).first()
bs = AuthorSerializers(obj, many=False)
return Response(bs.data)
# 修改
def put(self, request, pk):
obj = Author.objects.filter(pk=pk).first()
bs = AuthorSerializers(data=request.data, instance=obj)
if bs.is_valid():
bs.save()
return Response(bs.data)
else:
return Response(bs.errors)
# 删除
def delete(self, request, pk):
Author.objects.filter(pk=pk).delete()
return Response('')
GenericAPIView
GenericAPIView
类继承于 REST framework 的 APIView
类,为标准列表和详细视图添加了常见的行为。
内置的每一个具体的通用视图都是通过将 GenericAPIView
类和一个或多个 minxin 类相互结合来构建的。
Mixins
mixin 类用于提供基本视图行为的操作。请注意,mixin 类提供了操作方法,而不是直接定义处理方法,如 .get()
和 .post()
。这允许更灵活的行为组合。
mixin 类可以从 rest_framework.mixins
中导入。
ListModelMixin
提供一个 .list(request, *args, **kwargs)
方法,实现了列出一个查询集。
如果查询集已填充,则返回 200 OK
响应,并将 queryset 的序列化表示形式作为响应的主体。响应数据可以设置分页。
CreateModelMixin
提供 .create(request, *args, **kwargs)
方法,实现创建和保存新模型实例。
如果创建了一个对象,则会返回一个 201 Created
响应,并将该对象的序列化表示形式作为响应的主体。如果表示包含名为 url 的键,则响应的 Location
header 将填充该值。
如果为创建对象提供的请求数据无效,则将返回 400 Bad Request
响应,并将错误细节作为响应的主体。
RetrieveModelMixin
提供 .retrieve(request, *args, **kwargs)
方法,该方法实现在响应中返回现有的模型实例。
如果可以获取对象,则返回 200 OK
响应,并将对象的序列化表示作为响应的主体。否则,将返回一个 404 Not Found
。
UpdateModelMixin
提供 .update(request, *args, **kwargs)
方法,实现更新和保存现有模型实例。还提供了一个 .partial_update(request, *args, **kwargs)
方法,它与更新方法类似,只是更新的所有字段都是可选的。这允许支持 HTTP PATCH
请求。
如果成功更新对象,则返回 200 OK
响应,并将对象的序列化表示形式作为响应的主体。
如果提供的用于更新对象的请求数据无效,则将返回 400 Bad Request
响应,并将错误细节作为响应的主体。
DestroyModelMixin
提供一个 .destroy(request, *args, **kwargs)
方法,实现现有模型实例的删除。
如果一个对象被删除,则返回一个 204 No Content
,否则它将返回一个 404 Not Found
。
(2)我们用Mixins与GenericAPIview来实现
from app01.models import *
from app01.serializers import AuthorSerializers
from rest_framework.mixins import CreateModelMixin,ListModelMixin,DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin
from rest_framework import generics
class AuthorView(CreateModelMixin,ListModelMixin,generics.GenericAPIView):
queryset = Author.objects.all()
serializer_class = AuthorSerializers
def get(self,request):
return self.list(request)
def post(self,request):
return self.create(request)
class AuthorDetailView(RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin,generics.GenericAPIView):
queryset = Author.objects.all()
serializer_class = AuthorSerializers
def get(self,request,pk):
return self.retrieve(request,pk)
def delete(self,request,pk):
return self.destroy(request,pk)
def put(self,request,pk):
return self.update(request,pk)
我们来看一下CreateModelMixin
,ListModelMixin
,RetrieveModelMixin
,DestroyModelMixin
,UpdateModelMixin
这些类的源代码里写了什么:
class CreateModelMixin(object):
"""
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)
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 ListModelMixin(object):
"""
List a queryset.
"""
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)
class RetrieveModelMixin(object):
"""
Retrieve a model instance.
"""
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
class UpdateModelMixin(object):
"""
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()
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
class DestroyModelMixin(object):
"""
Destroy a model instance.
"""
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)
def perform_destroy(self, instance):
instance.delete()
(3)我们还可用genrics
里面的ListCreateAPIView
视图类和RetrieveUpdateDestroyAPIView
视图类进一步封装实现
from app01.models import *
from app01.serializers import AuthorSerializers
from rest_framework import generics
class AuthorView(generics.ListCreateAPIView):
queryset = Author.objects.all()
serializer_class = AuthorSerializers
class AuthorDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = Author.objects.all()
serializer_class = AuthorSerializers
我们来看一下ListCreateAPIView
和RetrieveUpdateDestroyAPIView
的源码里怎么写:
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)
class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
GenericAPIView):
"""
Concrete view for retrieving, updating or deleting a model instance.
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
别以为到这里就结束了,我们还有更精辟的方法!
ModelViewSet
ModelViewSet
类继承自 GenericAPIView
,并通过混合各种 mixin 类的行为来包含各种操作的实现。
ModelViewSet
提供的操作有 .list()
, .retrieve()
, .create()
, .update()
, .partial_update()
, 和 .destroy()
。由于 ModelViewSet
类继承自 GenericAPIView
,因此通常需要提供至少 queryset
和 serializer_class
属性。
(4) 基于ModelViewSet
来实现Author类的增删改查视图.(比较常用的一种方式)
from app01.models import *
from app01.serializers import AuthorSerializers
from rest_framework.viewsets import ModelViewSet
class AuthorModelView(ModelViewSet):
queryset = Author.objects.all()
serializer_class = AuthorSerializers
些时url里面要在as_view里面加上字典。
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^author/$',views.AuthorModelView.as_view({'get':'list','post':'create'})),
url(r'^author/(?P<pk>\d+)/$',views.AuthorModelView.as_view({'get':'retrieve','put':'update','delete':'destroy'})),
]
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
"""
pass
我们去看看GenericViewSet
里的源代码怎么写
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
"""
The GenericViewSet class does not provide any actions by default,
but does include the base set of generic view behavior, such as
the `get_object` and `get_queryset` methods.
"""
pass
我们去看看ViewSetMixin
里的关键部分源代码怎么写的
class ViewSetMixin(object):
...
@classonlymethod
#我们在as_view里写的字典会当成一个参数赋值给actions
def as_view(cls, actions=None, **initkwargs):
...
def view(request, *args, **kwargs):
self = cls(**initkwargs)
# 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循环是代码的关键部分,以actions={'get': 'list', 'post': 'create'}为例,第一次循环后self.get=self.list,第二次self.post=self.create.
'''
for method, action in actions.items():
handler = getattr(self, action)
setattr(self, method, handler)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
# And continue as usual
# 去APIView的dispatch完成分发
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.suffix = initkwargs.get('suffix', None)
view.actions = actions
return csrf_exempt(view)
...
再到APIView的dispatch看看分发情况:
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
def dispatch(self, request, *args, **kwargs):
...
try:
self.initial(request, *args, **kwargs)
# 关键代码
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
所以经过这样的一个巧妙的反射转变,就让dispatch分发get请求是直接去运行ListModelMixin
下的list
方法,其它请求也是一样。