目錄
一、實現數據庫的增刪改查 - 未優化常規寫法(兩個路由兩個視圖)
二、優化方式一 - GenericAPIView, ListModelMixin, CreateModelMixin……
2-3-2 ListModelMixin - 用於get獲取數據列表
2-3-4 RetrieveModelMixin - 獲取單個對象
2-3-5 UpdateModelMixin - 更新單個對象
2-3-6 DestroyModelMixin - 刪除對象
三、方式二- ListCreateAPIView、RetrieveUpdateDestroyAPIView
3-2-2 RetrieveUpdateDestroyAPIView
四、方式三 - ModelViewSet 配合 urls路由(不推薦)
一、實現數據庫的增刪改查 - 未優化常規寫法(兩個路由兩個視圖)
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