DRF(4)视图.md

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值