drf 視圖源碼詳解


mixin類和Generic類

導入

from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, ListModelMixin, UpdateModelMixin, DestroyModelMixin
from rest_framework.generics import GenericAPIView

CreateModelMixin 創建

源碼路徑為 rest_framework —> mixing.py —>CreateModelMixin

class CreateModelMixin(object):
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        # 傳入前端傳入的數據,進行反序列化
        serializer = self.get_serializer(data=request.data)
        
        # 判斷是否全部都校驗通過 通過了就執行下面語句
        # raise_exception=True 不通過直接報錯,下面也不會執行
        serializer.is_valid(raise_exception=True)
        
        # 執行perform_create 實際就是.save() 保存
        self.perform_create(serializer)
        
        # 頭部信息忽略
        headers = self.get_success_headers(serializer.data)
        
        # 遵循規范返回創建對象的數據
        # status 狀態信息,對應的是一個個狀態碼
        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 {}

視圖使用-post

class PublishView(CreateModelMixin,GenericAPIView):
  	queryset = models.Publish.objects.all()
    
  	# 內部執行的時候CreateModelMixin調用了serializer_class
    serializer_class = PublishSerializers
    
  		# 執行post請求就說明要添加數據
    def post(self, request, *args, **kwargs):
      # 執行create 自己沒有就執行CreateModelMixin 
      # 也就是上面的CreateModelMixin源碼解析
        return self.create(request, *args, **kwargs)

ListModelMixin - 查看多條數據

源碼路徑為 rest_framework —> mixing.py —>ListModelMixin

class ListModelMixin(object):
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        # 過濾相關
        # 這個時候調用了GenericAPIView內的get_queryset
        queryset = self.filter_queryset(self.get_queryset())
        
        # 分頁相關
        page = self.paginate_queryset(queryset)

        # page不為空的情況下
        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)

視圖-get-取所有數據

class PublishView(ListModelMixin,GenericAPIView):
  	queryset = models.Publish.objects.all()
    serializer_class = PublishSerializers
    
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

RetrieveModelMixin 獲取單條數據

源碼路徑為 rest_framework —> mixing.py—>RetrieveModelMixin

class RetrieveModelMixin(object):

    def retrieve(self, request, *args, **kwargs):
        # 獲取單條
        instance = self.get_object()
        # 獲取到序列化返回
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

視圖-get-取指定數據

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

    # 獲取指定一條
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

UpdateModelMixin 更新單條數據

源碼路徑為 rest_framework —> mixing.py—>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):
            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)

視圖 - put- 更新指定數據

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

    # 更新指定數據
    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

DestroyModelMixin - 刪除指定數據

class DestroyModelMixin(object):
    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()

視圖 - delete- 刪除指定數據

class PublishDetailView(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView):
    queryset = models.Publish.objects.all()
    serializer_class = PublishSerializers
    
    # 刪除指定數據
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

GenericAPIView 通用的apiview

get_queryset(self):

def get_queryset(self):
    assert self.queryset is not None, (
        "'%s' should either include a `queryset` attribute, "
        "or override the `get_queryset()` method."
        % self.__class__.__name__
    )
    # 這個時候的queryset 是傳遞進來的queryset對象
    queryset = self.queryset
    # 判斷是否是QuerySet,不是QuerySet 自動.all()
    # 說明queryset可以直接給它傳遞一個對象如 models.Publish.objects
    if isinstance(queryset, QuerySet):
        # Ensure queryset is re-evaluated on each request.
        queryset = queryset.all()
        # 返回queryset對象
    return queryset

generics 里面組合了mixins里面方法和GenericAPIView

導入

from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView

url

url(r'^publish/$', views.publishView.as_view()),
url(r'^publish/(?P<pk>\d+)/$', views.PublishDetailView.as_view())

ListCreateAPIView源碼 創建與查詢多條

class ListCreateAPIView(mixins.ListModelMixin,
                        mixins.CreateModelMixin,
                        GenericAPIView):
  	# 獲取多條
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)
		# 創建數據
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

RetrieveUpdateDestroyAPIView源碼 查詢單條 更新 刪除

class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
                                   mixins.UpdateModelMixin,
                                   mixins.DestroyModelMixin,
                                   GenericAPIView):
		# 獲取指定數據
    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)

視圖

from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView

class publishView(ListCreateAPIView):
    queryset=models.Publish.objects.all()
    serializer_class=PublishSerializers

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

ModelViewSet 多種請求映射

導入:

from rest_framework.viewsets import ModelViewSet
  • 路由:
    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'})),
  • 視圖:
from rest_framework.viewsets import ModelViewSet

class PublishView(ModelViewSet):
    queryset=models.Publish.objects.all()
    serializer_class=PublishSerializers
    
# 這里面雖然可以滿足方法的請求但是有時候我們會更具業務的需要,去修改,我們可以重寫指定的方法,如create等

為什么可以可以做到請求映射?

  • 主要是重寫了as_view方法

1.ModelViewSet中唯一不通的就是繼承了GenericViewSet,上面的方法中繼承的是GenericAPIView 倆者不同

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    pass

2.GenericViewSet中pass??,繼承了之前的GenericAPIView,但是多繼承了一個ViewSetMixin

class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
    pass

3.ViewSetMixin中仔細看as_view 和原生的Django.views.View.as_view的區別,多了一個參數

Django.views.View.as_view
def as_view(cls, **initkwargs):
  	pass
  
ViewSetMixin.as_view  
def as_view(cls, actions=None, **initkwargs):
  	pass
 

4.所以路由中配置的字典{'get': 'list', 'post': 'create'}給了actions

 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'})),

5.ViewSetMixin.as_view 源碼中如果可以看出如果繼承了ViewSetMixin沒有傳遞actions直接就會異常

if not actions:
    raise TypeError("The `actions` argument must be provided when "
                    "calling `.as_view()` on a ViewSet. For example "
                    "`.as_view({'get': 'list'})`")

6.ViewSetMixin.as_view核心代碼,也就是這一段代碼讓我們的請求直接執行對應的執行函數

def view(request, *args, **kwargs):
    self = cls(**initkwargs)
    # 調用了傳入的actions 賦值給了action_map
    self.action_map = actions
    
    # {'get': 'list', 'post': 'create'}
    # 解壓賦值 請求給了method 處理參數給了action
    for method, action in actions.items():
        # self是自定義的那個路由函數的對象
        # 通過反射去對象中找action
        # handler就相當於 那個執行方法的內存地址
        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
    return self.dispatch(request, *args, **kwargs)

自定義ViewSetMixin類

  • 相當於映射 指定請求來了執行指定方法
from rest_framework.viewsets import ViewSetMixin
from rest_framework.views import APIView

# 要注意繼承的順序如果APIView繼承在前按照mro列表先執行它里面的as_views,所以要放在前面
class Test(ViewSetMixin, APIView):
    def test(self, request, pk):
        print('自定義請求收到參數:', pk)
        return Response()

url

url(r'^test/(?P<pk>\d+)/$', views.Test.as_view({'get': 'test'})),


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM