一、自定义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 self.get_paginated_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
验证结果: