Django Rest框架:dynamic返回字段的子集
问题
正如在devise一个实用的RESTful API的blogpost 最佳实践中推荐的,我想添加一个fields
查询参数给一个基于Django Rest Framework的API,使用户只能为每个资源select一个字段子集。
例
串行:
class IdentitySerializer(serializers.HyperlinkedModelSerializer): class Meta: model = models.Identity fields = ('id', 'url', 'type', 'data')
常规查询将返回所有字段。
GET /identities/
[ { "id": 1, "url": "http://localhost:8000/api/identities/1/", "type": 5, "data": "John Doe" }, ... ]
带有fields
参数的查询应只返回字段的一个子集:
GET /identities/?fields=id,data
[ { "id": 1, "data": "John Doe" }, ... ]
具有无效字段的查询应忽略无效字段或抛出客户端错误。
目标
不知何故,这是可能的吗? 如果不是,那么实现这个最简单的方法是什么? 这是否有第三方软件包呢?
您可以重写序列化程序__init__
方法,并根据查询参数设置fields
属性dynamicaly。 您可以访问request
对象,并将其传递给序列化程序。
在这里,我创build了一个可重用的混合,即dynamicfields
修改。
from rest_framework import serializers class DynamicFieldsModelSerializer(serializers.ModelSerializer): """ A ModelSerializer that takes an additional `fields` argument that controls which fields should be displayed. """ def __init__(self, *args, **kwargs): # Instantiate the superclass normally super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs) fields = self.context['request'].QUERY_PARAMS.get('fields') if fields: fields = fields.split(',') # Drop any fields that are not specified in the `fields` argument. allowed = set(fields) existing = set(self.fields.keys()) for field_name in existing - allowed: self.fields.pop(field_name) class UserSerializer(DynamicFieldsModelSerializer, serializers.HyperlinkedModelSerializer): class Meta: model = User fields = ('url', 'username', 'email')
2016年答案:
这个有用的APIfunction类似于其他框架文档中显示的配方 ,现在可以作为第三方软件包提供。
pip install djangorestframework-queryfields
如果你声明你的序列化器是这样的:
from rest_framework.serializers import ModelSerializer from drf_queryfields import QueryFieldsMixin class MyModelSerializer(QueryFieldsMixin, ModelSerializer): ...
然后现在可以通过使用查询参数来指定这些字段(客户端):
GET /identities/?fields=id,data
排除过滤也是可能的,例如返回除了 id 之外的每个字段:
GET /identities/?fields!=id
免责声明:我是djangorestframework-queryfields的维护者 。
serializers.py
class DynamicFieldsSerializerMixin(object): def __init__(self, *args, **kwargs): # Don't pass the 'fields' arg up to the superclass fields = kwargs.pop('fields', None) # Instantiate the superclass normally super(DynamicFieldsSerializerMixin, self).__init__(*args, **kwargs) if fields is not None: # Drop any fields that are not specified in the `fields` argument. allowed = set(fields) existing = set(self.fields.keys()) for field_name in existing - allowed: self.fields.pop(field_name) class UserSerializer(DynamicFieldsSerializerMixin, serializers.HyperlinkedModelSerializer): password = serializers.CharField( style={'input_type': 'password'}, write_only=True ) class Meta: model = User fields = ('id', 'username', 'password', 'email', 'first_name', 'last_name') def create(self, validated_data): user = User.objects.create( username=validated_data['username'], email=validated_data['email'], first_name=validated_data['first_name'], last_name=validated_data['last_name'] ) user.set_password(validated_data['password']) user.save() return user
views.py
class DynamicFieldsViewMixin(object): def get_serializer(self, *args, **kwargs): serializer_class = self.get_serializer_class() fields = None if self.request.method == 'GET': query_fields = self.request.QUERY_PARAMS.get("fields", None) if query_fields: fields = tuple(query_fields.split(',')) kwargs['context'] = self.get_serializer_context() kwargs['fields'] = fields return serializer_class(*args, **kwargs) class UserList(DynamicFieldsViewMixin, ListCreateAPIView): queryset = User.objects.all() serializer_class = UserSerializer
configuration新的分页序列化程序类
from rest_framework import pagination, serializers class DynamicFieldsPaginationSerializer(pagination.BasePaginationSerializer): """ A dynamic fields implementation of a pagination serializer. """ count = serializers.Field(source='paginator.count') next = pagination.NextPageField(source='*') previous = pagination.PreviousPageField(source='*') def __init__(self, *args, **kwargs): """ Override init to add in the object serializer field on-the-fly. """ fields = kwargs.pop('fields', None) super(pagination.BasePaginationSerializer, self).__init__(*args, **kwargs) results_field = self.results_field object_serializer = self.opts.object_serializer_class if 'context' in kwargs: context_kwarg = {'context': kwargs['context']} else: context_kwarg = {} if fields: context_kwarg.update({'fields': fields}) self.fields[results_field] = object_serializer(source='object_list', many=True, **context_kwarg) # Set the pagination serializer setting REST_FRAMEWORK = { # [...] 'DEFAULT_PAGINATION_SERIALIZER_CLASS': 'DynamicFieldsPaginationSerializer', }
制作dynamic串行器
from rest_framework import serializers class DynamicFieldsModelSerializer(serializers.ModelSerializer): """ A ModelSerializer that takes an additional `fields` argument that controls which fields should be displayed. See: http://tomchristie.github.io/rest-framework-2-docs/api-guide/serializers """ def __init__(self, *args, **kwargs): # Don't pass the 'fields' arg up to the superclass fields = kwargs.pop('fields', None) # Instantiate the superclass normally super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs) if fields: # Drop any fields that are not specified in the `fields` argument. allowed = set(fields) existing = set(self.fields.keys()) for field_name in existing - allowed: self.fields.pop(field_name) # Use it class MyPonySerializer(DynamicFieldsModelSerializer): # [...]
最后,为你的APIViews使用一个homemage mixin
class DynamicFields(object): """A mixins that allows the query builder to display certain fields""" def get_fields_to_display(self): fields = self.request.GET.get('fields', None) return fields.split(',') if fields else None def get_serializer(self, instance=None, data=None, files=None, many=False, partial=False, allow_add_remove=False): """ Return the serializer instance that should be used for validating and deserializing input, and for serializing output. """ serializer_class = self.get_serializer_class() context = self.get_serializer_context() fields = self.get_fields_to_display() return serializer_class(instance, data=data, files=files, many=many, partial=partial, allow_add_remove=allow_add_remove, context=context, fields=fields) def get_pagination_serializer(self, page): """ Return a serializer instance to use with paginated data. """ class SerializerClass(self.pagination_serializer_class): class Meta: object_serializer_class = self.get_serializer_class() pagination_serializer_class = SerializerClass context = self.get_serializer_context() fields = self.get_fields_to_display() return pagination_serializer_class(instance=page, context=context, fields=fields) class MyPonyList(DynamicFields, generics.ListAPIView): # [...]
请求
现在,当您请求资源时,您可以添加一个参数fields
以仅显示url中指定的字段。 /?fields=field1,field2
你可以在这里find提醒: https : //gist.github.com/Kmaschta/e28cf21fb3f0b90c597a
我们在drf_tweaks / control-over-serialized-fields中提供了这样的function。
如果您使用我们的序列化程序,则只需在查询中传递?fields=x,y,z
参数。