Django - DRF - 视图优化组件

目录

一、实现数据库的增删改查 - 未优化常规写法(两个路由两个视图)

1-1 两条路由分发 - 接参不接参

 1-2 Serializers用于获取数据序列化

1-3 视图函数

二、优化方式一 - GenericAPIView, ListModelMixin, CreateModelMixin……

2-1 视图函数 + 路由设计

2-2 视图优化思路 - 单独提出重复代码,封装成类及方法

2-3 源码分析

2-3-1   GenericAPIView  - 视图优化基类

2-3-2  ListModelMixin - 用于get获取数据列表

2-3-3 CreateModelMixin - 创建对象

2-3-4 RetrieveModelMixin - 获取单个对象

2-3-5 UpdateModelMixin - 更新单个对象

2-3-6  DestroyModelMixin - 删除对象

三、方式二- ListCreateAPIView、RetrieveUpdateDestroyAPIView

3-1 视图函数

3-2 源码分析

3-2-1 ListCreateAPIView

3-2-2 RetrieveUpdateDestroyAPIView 

3-2-3  RetrieveDestroyAPIView

3-2-4 RetrieveUpdateAPIView

四、方式三 - ModelViewSet 配合 urls路由(不推荐)

4-1 路由设计

4-2 视图函数

4-3 源码分析


一、实现数据库的增删改查 - 未优化常规写法(两个路由两个视图)

1-1 两条路由分发 - 接参不接参

from django.conf.urls import url,include
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),

    url(r'^publish/$', views.PublishView.as_view()),
    url(r'^publish/(?P\d+)', views.PublishDetailView.as_view()),
]

 1-2 Serializers用于获取数据序列化

from rest_framework import serializers
from app01 import models

class PublishSerializers(serializers.ModelSerializer):
    class Meta:
        model = models.Publish
        fields = '__all__'

1-3 视图函数

from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from django.http import JsonResponse
from app01 import MySerializer

class PublishView(APIView):
    def get(self, request):
        publish_list = models.Publish.objects.all()
        ser = MySerializer.PublishSerializers(publish_list, many=True)
        return JsonResponse(ser.data, safe=False)

    def post(self, request):
        ser = MySerializer.PublishSerializers(data=request.data)
        if ser.is_valid():
            ser.save()
            return JsonResponse(ser.data, safe=False)
        else:
            return JsonResponse(ser.errors, safe=False)


class PublishDetailView(APIView):
    def get(self, request, pk):
        publish_obj = models.Publish.objects.filter(pk=pk).first()
        ser = MySerializer.PublishSerializers(publish_obj, many=False)
        return JsonResponse(ser.data, safe=False)

    def put(self, request, pk):
        publish_obj = models.Publish.objects.filter(pk=pk).first()
        ser = MySerializer.PublishSerializers(data=request.data, instance=publish_obj)
        if ser.is_valid():
            ser.save()
            return JsonResponse(ser.data, safe=False)
        else:
            return JsonResponse(ser.errors, safe=False)

    def delete(self, request, pk):
        models.Publish.objects.filter(pk=pk).delete()
        return JsonResponse("delete", safe=False)

二、优化方式一 - GenericAPIView, ListModelMixin, CreateModelMixin……

总结:

  • ListModelMixin - 获取对象列表
  • CreateModelMixin - 创建对象数据
  • RetrieveModelMixin - 获取指定对象数据
  • UpdateModelMixin - 更新对象数据
  • DestroyModelMixin - 删除对象数据

2-1 视图函数 + 路由设计

'''
from django.conf.urls import url,include
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^publish/$', views.PublishView.as_view()),
    url(r'^publish/(?P\d+)', views.PublishDetailView.as_view()),
]

'''

from app01 import models
from app01 import MySerializer
from rest_framework.mixins import ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, \
    DestroyModelMixin

from rest_framework.generics import GenericAPIView


class PublishView(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = models.Publish.objects.all()
    serializer_class = MySerializer.PublishSerializers

    def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)


class PublishDetailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = models.Publish.objects.all()
    serializer_class = MySerializer.PublishSerializers

    def get(self, request, pk):
        return self.retrieve(request, pk)

    def put(self, request, pk):
        return self.update(request, pk)

    def delete(self, request, pk):
        return self.destroy(request, pk)

2-2 视图优化思路 - 单独提出重复代码,封装成类及方法

'''
from django.conf.urls import url,include
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^publish/$', views.PublishView.as_view()),
    url(r'^publish/(?P\d+)', views.PublishDetailView.as_view()),
]

'''

'''
数据库的增删改查逻辑,提出来封装成类,以及类内的方法
'''
class List:
    def list(self, request):
        # queryset : Publish.objects.all(),serializers : PublishSerializers
        queryset = self.queryset.all()
        ser = self.serializers(queryset, many=True)
        return JsonResponse(ser.data, safe=False)


class Create:
    def create(self, request):
        print(request.data)

        ser = MySerializer.PublishSerializers(data=request.data)
        if ser.is_valid():
            ser.save()  # 生成记录
            return JsonResponse(ser.data, safe=False)
        else:

            return JsonResponse(ser.errors, safe=False)


class Retrieve:
    def retrieve(self, request, pk):
        queryset = self.queryset.all()
        obj = queryset.filter(pk=pk).first()
        ser = self.serializers(obj, many=False)
        return JsonResponse(ser.data, safe=False)


class Update:
    def update(self, request, pk):
        obj = self.queryset.filter(pk=pk).first()
        ser = self.serializers(data=request.data, instance=obj, many=False)
        if ser.is_valid():
            ser.save()
            return JsonResponse(ser.data, safe=False)
        else:
            return JsonResponse(ser.errors, safe=False)


class Destroy:
    def destroy(self, request, pk):
        self.queryset.filter(pk=pk).delete()
        return JsonResponse("delete", safe=False)


class PublishView(APIView, List, Create):
    queryset = models.Publish.objects.all()
    serializers = MySerializer.PublishSerializers

    def get(self, request):
        return self.list(request)

    def post(self, request):
        # 添加一条数据
        return self.create(request)


class PublishDetailView(APIView, Retrieve, Update, Destroy):
    queryset = models.Publish.objects.all()
    serializers = MySerializer.PublishSerializers

    def get(self, request, pk):
        return self.retrieve(request, pk)

    def put(self, request, pk):
        return self.update(request, pk)

    def delete(self, request, pk):
        return self.destroy(request, pk)

2-3 源码分析

2-3-1   GenericAPIView  - 视图优化基类

class GenericAPIView(views.APIView):
    """
    Base class for all other generic views.
    """
    # You'll need to either set these attributes,
    # or override `get_queryset()`/`get_serializer_class()`.
    # If you are overriding a view method, it is important that you call
    # `get_queryset()` instead of accessing the `queryset` property directly,
    # as `queryset` will get evaluated only once, and those results are cached
    # for all subsequent requests.
    '''
    您需要设置这些属性,或者覆盖' get_queryset() ' / ' get_serializer_class() '。
如果你覆盖了一个视图方法,重要的是你调用' get_queryset() '而不是直接访问' queryset '属性,
因为‘queryset’只会被求值一次,而这些结果会被缓存到所有后续请求中。
    '''
    queryset = None
    serializer_class = None

    # If you want to use object lookups other than pk, set 'lookup_field'.
    # For more complex lookup requirements override `get_object()`.
    '''
    如果您想使用pk之外的对象查找,请设置“lookup_field”。
    对于更复杂的查询需求,请重写' get_object() '。
    '''
    lookup_field = 'pk'
    lookup_url_kwarg = None

    # The filter backend classes to use for queryset filtering
    # 用于queryset筛选的筛选后端类
    filter_backends = api_settings.DEFAULT_FILTER_BACKENDS

    # The style to use for queryset pagination.
    # 用于queryset分页的样式。
    pagination_class = api_settings.DEFAULT_PAGINATION_CLASS

    def get_queryset(self):
        """
        Get the list of items for this view.
        This must be an iterable, and may be a queryset.
        Defaults to using `self.queryset`.

        This method should always be used rather than accessing `self.queryset`
        directly, as `self.queryset` gets evaluated only once, and those results
        are cached for all subsequent requests.

        You may want to override this if you need to provide different
        querysets depending on the incoming request.

        (Eg. return a list of items that is specific to the user)
        """
        assert self.queryset is not None, (
            "'%s' should either include a `queryset` attribute, "
            "or override the `get_queryset()` method."
            % self.__class__.__name__
        )

        queryset = self.queryset
        if isinstance(queryset, QuerySet):
            # Ensure queryset is re-evaluated on each request.
            queryset = queryset.all()
        return queryset

    def get_object(self):
        """
        Returns the object the view is displaying.

        You may want to override this if you need to provide non-standard
        queryset lookups.  Eg if objects are referenced using multiple
        keyword arguments in the url conf.
        """
        queryset = self.filter_queryset(self.get_queryset())

        # Perform the lookup filtering.
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

        assert lookup_url_kwarg in self.kwargs, (
            'Expected view %s to be called with a URL keyword argument '
            'named "%s". Fix your URL conf, or set the `.lookup_field` '
            'attribute on the view correctly.' %
            (self.__class__.__name__, lookup_url_kwarg)
        )

        filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
        obj = get_object_or_404(queryset, **filter_kwargs)

        # May raise a permission denied
        self.check_object_permissions(self.request, obj)

        return obj

    def get_serializer(self, *args, **kwargs):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
        return serializer_class(*args, **kwargs)

    def filter_queryset(self, queryset):
        """
        Given a queryset, filter it with whichever filter backend is in use.

        You are unlikely to want to override this method, although you may need
        to call it either from a list view, or from a custom `get_object`
        method if you want to apply the configured filtering backend to the
        default queryset.
        给定一个queryset,用使用的任何过滤器后端过滤它。
        您不太可能想要覆盖这个方法,尽管您可能需要要从列表视图或自定义的“get_object”调用它,
        请执行以下操作方法中应用已配置的筛选后端。默认queryset。
        """
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset

2-3-2  ListModelMixin - 用于get获取数据列表

class ListModelMixin(object):
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        # GenericAPIView内的filter_queryset和get_queryset
        # 主要通过筛选等逻辑处理获取了一个queryset
        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)
        # GenericAPIView内的get_serializer
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

2-3-3 CreateModelMixin - 创建对象

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)
        # 调用下方perform_create
        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 {}

2-3-4 RetrieveModelMixin - 获取单个对象

class RetrieveModelMixin(object):
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        # GenericAPIView的get_object方法,获取单个对象
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)



def get_object_or_404(queryset, *filter_args, **filter_kwargs):
    """
    Same as Django's standard shortcut, but make sure to also raise 404
    if the filter_kwargs don't match the required types.
    """
    try:
        return _get_object_or_404(queryset, *filter_args, **filter_kwargs)
    except (TypeError, ValueError, ValidationError):
        raise Http404

2-3-5 UpdateModelMixin - 更新单个对象

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)

2-3-6  DestroyModelMixin - 删除对象

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()

三、方式二- ListCreateAPIView、RetrieveUpdateDestroyAPIView

ListCreateAPIView - 等同于GenericAPIView, ListModelMixin, CreateModelMixin

RetrieveUpdateDestroyAPIView - 等同于GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin

3-1 视图函数

from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
from app01 import MySerializer
from app01 import models

# 同下 class PublishView(GenericAPIView, ListModelMixin, CreateModelMixin):
class PublishView(ListCreateAPIView):
    queryset = models.Publish.objects.all()
    serializer_class = MySerializer.PublishSerializers


class PublishDetailView(RetrieveUpdateDestroyAPIView):
    queryset = models.Publish.objects.all()
    serializer_class = MySerializer.PublishSerializers

3-2 源码分析

3-2-1 ListCreateAPIView

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)

3-2-2 RetrieveUpdateDestroyAPIView 

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)

3-2-3  RetrieveDestroyAPIView

class RetrieveDestroyAPIView(mixins.RetrieveModelMixin,
                             mixins.DestroyModelMixin,
                             GenericAPIView):
    """
    Concrete view for retrieving or deleting a model instance.
    """
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

3-2-4 RetrieveUpdateAPIView

class RetrieveUpdateAPIView(mixins.RetrieveModelMixin,
                            mixins.UpdateModelMixin,
                            GenericAPIView):
    """
    Concrete view for retrieving, updating 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)

 

四、方式三 - ModelViewSet 配合 urls路由(不推荐)

4-1 路由设计

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),

    # 基于ViewSetMixin,重写as_view方法拦截
    url(r'^publish/$', views.PublishView.as_view({'get': 'list', 'post': 'create'})),
    url(r'^publish/(?P\d+)', views.PublishView.as_view({'get': 'retrieve', 'put': 'update','delete':'destroy'})),

]

4-2 视图函数

from rest_framework.viewsets import ModelViewSet


# ModelViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin,
#              mixins.DestroyModelMixin,
#              mixins.ListModelMixin,
#              GenericViewSet):
class PublishView(ModelViewSet):
    queryset = models.Publish.objects.all()
    serializer_class = MySerializer.PublishSerializers

4-3 源码分析

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

 

你可能感兴趣的:(Django,DRF)