REST framework(三) 视图组件

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

我们来看一下ListCreateAPIViewRetrieveUpdateDestroyAPIView的源码里怎么写:

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,因此通常需要提供至少 querysetserializer_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方法,其它请求也是一样。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值