1. 程式人生 > 實用技巧 >DRF框架之自定義action

DRF框架之自定義action

一、自定義action

  • 使用action裝飾器
  • methods
    • 支援的請求方式,為一個列表,預設為['get']
  • detail
    • 必傳引數,
    • 要處理的是否是詳情資源物件(即是否通過url路徑獲取主鍵)
    • True表示需要傳遞主鍵id,使用通過URL獲取的主鍵對應的資料物件
    • False表示不需要傳遞主鍵id,不使用URL獲取主鍵
  • url_path
    • 指定url部分,預設為action名稱
  • url_name
    • 指定url的名稱,預設為action名稱

二、action原始碼

def action(methods=None, detail=None, url_path=None, url_name=None, **kwargs):
    
""" Mark a ViewSet method as a routable action. `@action`-decorated functions will be endowed with a `mapping` property, a `MethodMapper` that can be used to add additional method-based behaviors on the routed action. :param methods: A list of HTTP method names this action responds to. Defaults to GET only. :param detail: Required. Determines whether this action applies to instance/detail requests or collection/list requests. :param url_path: Define the URL segment for this action. Defaults to the name of the method decorated. :param url_name: Define the internal (`reverse`) URL name for this action. Defaults to the name of the method decorated with underscores replaced with dashes. :param kwargs: Additional properties to set on the view. This can be used to override viewset-level *_classes settings, equivalent to how the `@renderer_classes` etc. decorators work for function- based API views.
""" methods = ['get'] if (methods is None) else methods methods = [method.lower() for method in methods] assert detail is not None, ( "@action() missing required argument: 'detail'" ) # name and suffix are mutually exclusive if 'name' in kwargs and 'suffix' in kwargs:
raise TypeError("`name` and `suffix` are mutually exclusive arguments.") def decorator(func): func.mapping = MethodMapper(func, methods) func.detail = detail func.url_path = url_path if url_path else func.__name__ func.url_name = url_name if url_name else func.__name__.replace('_', '-') # These kwargs will end up being passed to `ViewSet.as_view()` within # the router, which eventually delegates to Django's CBV `View`, # which assigns them as instance attributes for each request. func.kwargs = kwargs # Set descriptive arguments for viewsets if 'name' not in kwargs and 'suffix' not in kwargs: func.kwargs['name'] = pretty_name(func.__name__) func.kwargs['description'] = func.__doc__ or None return func return decorator

三、使用方法

1.引入action

from rest_framework.decorators import action

2.定義一個序列化器類

from rest_framework import serializers
from .models import Projects


class ProjectsNamesModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = Projects
        fields = ('id', 'name')

3.自定義action方法

使用裝飾器@action(),傳入methods和detail等引數值

@action(methods=['get'], detail=False)
    def names(self, request):
        qs = self.filter_queryset(self.get_queryset())
        page = self.paginate_queryset(qs)
        if page:
            serializer_obj = self.get_serializer(instance=page, many=True)
            return Response(serializer_obj.data)
        serializer_obj = self.get_serializer(instance=qs, many=True)
        return Response(serializer_obj.data)

4.重寫get_serializer_class方法

使用self.action可以獲取到傳入的action,因此可以使用if判斷來滿足特定條件下返回不同的序列化器類

    def get_serializer_class(self):
        if self.action == 'names':
            return ProjectsNamesModelSerializer
        else:
            return self.serializer_class

5.配置路由資訊

from django.contrib import admin
from django.urls import path
from projects.views import ProjectsPageSet


urlpatterns = [
    path('admin/', admin.site.urls)
    path('projects/names/', ProjectsPageSet.as_view({
        'get': 'names'
    }))
]

驗證結果:

四、從表關聯

需求:指定獲取某個專案下對應的從表的id和name?

1.定義序列化器類

from rest_framework import serializers
from .models import Projects
from interfaces.models import Interfaces


class InterfacesNamesModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = Interfaces
        fields = ('id', 'name')


class InterfacesByProjectIdModelSerializer(serializers.ModelSerializer):
    interfaces = InterfacesNamesModelSerializer(many=True, read_only=True, label='從表id', help_text='從表id')

    class Meta:
        model = Projects
        fields = ('id', 'interfaces')

2.自定義action方法

@action(methods=['get'], detail=True)
    def interfaces(self, request, *args, **kwargs):
        qs = self.get_object()
        serializer_obj = self.get_serializer(instance=qs)
        return Response(serializer_obj.data)

3.重寫get_serializer_class方法

    def get_serializer_class(self):
        if self.action == 'interfaces':
            return InterfacesByProjectIdModelSerializer
        else:
            return self.serializer_class

驗證結果: