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<pk>\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<pk>\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<pk>\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<pk>\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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章