Django Rest Framework : 동적으로 필드 하위 집합 반환
문제
블로그 게시물 실용적인 RESTful API를 설계하기위한 모범 사례 에서 권장 fields
하는대로 Django Rest Framework 기반 API에 쿼리 매개 변수를 추가 하여 사용자가 리소스 당 필드의 하위 집합 만 선택할 수 있고 싶습니다 .
예
내장 변환기 :
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
하고 쿼리 매개 변수를-based으로 속성을 동적으로 설정할 수 있습니다 . request
serializer에 전달 된 메시징 전체 에서 개체에 액세스 할 수 있습니다 .
다음은 문제에 대한 Django Rest Framework 문서 예제 의 복사 및 가져 오기입니다 .
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')
이 기능은 기본 패키지 에서 사용할 수 있습니다 .
pip install djangorestframework-queryfields
다음과 같이 serializer를 선언하십시오.
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
면책 조항 : 저는 저자 / 관리자입니다.
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
새 페이지 매김 변환기 클래스 구성
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',
}
동적 생성 변환기 만들기
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):
# [...]
마지막으로 APIView에 홈 마지 믹스 인을 사용하세요.
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
여기에서 알림을 찾을 수 있습니다 : https://gist.github.com/Kmaschta/e28cf21fb3f0b90c597a
이러한 기능은 drf_tweaks / control-over-serialized-fields에서 제공했습니다 .
직렬 변환기를 사용하는 경우 ?fields=x,y,z
쿼리에서 매개 변수 를 전달 하기 만하면됩니다.
동적 필드 (포함, 제외), 포함 / 사이드로드 된 개체, 필터링, 순서 지정, 페이지 매김 등을 지원하는 Dynamic REST를 사용해 볼 수 있습니다.
중첩 데이터의 경우 docs , drf-flexfields 에서 권장하는 패키지와 함께 Django Rest Framework를 사용하고 있습니다.
이를 통해 부모 및 자식 개체 모두에서 반환되는 필드를 제한 할 수 있습니다. Readme의 지침은 훌륭하며주의해야 할 몇 가지 사항입니다.
URL에 readme '/ person? expand = country & fields = id, name, country'대신 '/ person /? expand = country & fields = id, name, country'와 같은 /가 필요한 것 같습니다.
중첩 된 개체의 이름과 관련 이름은 완전히 일관되어야하며 그렇지 않은 경우에는 필요하지 않습니다.
예를 들어 국가가 여러 주를 가질 수있는 '다'가있는 경우 문서에 설명 된대로 Serializer에서 '다': True를 설정해야합니다.
GraphQL과 같은 유연한 것을 원한다면 django-restql을 사용할 수 있습니다 . 중첩 된 데이터 (플랫 및 반복 가능)를 지원합니다.
예
from rest_framework import serializers
from django.contrib.auth.models import User
from django_restql.mixins import DynamicFieldsMixin
class UserSerializer(DynamicFieldsMixin, serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', 'groups')
일반 요청은 모든 필드를 반환합니다.
GET /users
[
{
"id": 1,
"username": "yezyilomo",
"email": "yezileliilomo@hotmail.com",
"groups": [1,2]
},
...
]
query
반면에 매개 변수가 있는 요청 은 필드의 하위 집합 만 반환합니다.
GET /users/?query={id, username}
[
{
"id": 1,
"username": "yezyilomo"
},
...
]
django-restql 을 사용하면 모든 수준의 중첩 필드에 액세스 할 수 있습니다. 예
GET /users/?query={id, username, date_joined{year}}
[
{
"id": 1,
"username": "yezyilomo",
"date_joined": {
"year": 2018
}
},
...
]
반복 가능한 중첩 필드의 경우 (예 : 사용자 그룹).
GET /users/?query={id, username, groups{id, name}}
[
{
"id": 1,
"username": "yezyilomo",
"groups": [
{
"id": 2,
"name": "Auth_User"
}
]
},
...
]
'IT' 카테고리의 다른 글
iOS에서 UIView를 PDF로 변환하는 방법은 무엇입니까? (0) | 2020.09.16 |
---|---|
ggplot2로 만든 전체의 배경색을 어떻게 변경합니까? (0) | 2020.09.16 |
레이아웃을 확장하여 사용자 지정보기를 만드시겠습니까? (0) | 2020.09.16 |
자바 영역을 GMT / UTC로 강제 적용 (0) | 2020.09.16 |
JPA : 단방향 다 대일 및 계단식 삭제 (0) | 2020.09.16 |