Django रेस्ट फ्रेमवर्क: गतिशील रूप से खेतों का सबसेट वापस


101

संकट

जैसा कि ब्लॉगपोस्ट बेस्ट प्रैक्टिसेज इन द डिजाइनिंग ए प्रैस्टेटिक रैस्टफुल एपीआई की सिफारिश की गई है , मैं एक fieldsक्वेरी पैरामीटर को एक Django रेस्ट फ्रेमवर्क आधारित एपीआई में जोड़ना चाहूंगा जो उपयोगकर्ता को प्रति संसाधन केवल फ़ील्ड्स के एक सबसेट का चयन करने में सक्षम बनाता है।

उदाहरण

serializer:

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"
  },
  ...
]

अमान्य फ़ील्ड वाली क्वेरी को या तो अमान्य फ़ील्ड्स को अनदेखा करना चाहिए या क्लाइंट त्रुटि फेंकनी चाहिए।

लक्ष्य

क्या यह किसी भी तरह से संभव है? यदि नहीं, तो इसे लागू करने का सबसे सरल तरीका क्या है? वहाँ एक 3 पार्टी पैकेज है कि चारों ओर यह पहले से ही है?

जवाबों:


123

आप क्रमांक __init__विधि को ओवरराइड कर सकते हैं और fieldsक्वेरी पारम के आधार पर विशेषता को गतिशील रूप से सेट कर सकते हैं । आप requestपूरे संदर्भ में ऑब्जेक्ट तक पहुंच सकते हैं , जो धारावाहिक के लिए पारित किया गया है।

यहाँ मामले पर Django बाकी फ्रेमवर्क प्रलेखन उदाहरण से एक प्रति और पेस्ट है:

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

4
मैं आखिरकार इसे लागू करने के लिए चारों ओर आया, और यह पूरी तरह से काम करता है! धन्यवाद। मैंने इसके लिए एक मिश्रक लिखना समाप्त किया, रचना उपवर्ग की तुलना में थोड़ी अधिक लचीली है :) gist.github.com/dbrgn/4e6fc1fe5922598592d6
Danilo Bargen

8
आप को बदलना होगा QUERY_PARAMSकरने के लिए query_paramsDjango के हाल के संस्करणों में लेकिन, की तुलना में है कि यह एक आकर्षण की तरह काम करता अन्य।
Myk विलिस

3
आप शायद जाँच लें कि requestsएक सदस्य के रूप में मौजूद है context। हालांकि यह उत्पादन में ऐसा करता है जब यूनिट परीक्षणों को चलाने से ऐसा नहीं होता है जो वस्तुओं को मैन्युअल रूप से बनाते हैं।
सेप्टेक

21
FYI करें: यह उदाहरण यहाँ पाया गया DRF प्रलेखन की एक शब्दशः प्रतिलिपि है: django-rest-framework.org/api-guide/serializers/#example यह मूल लेखकों को लिंक प्रदान नहीं करने के लिए एक बुरा रूप है
अलेकस्क

3
डीआरएफ प्रलेखन , जिसमें से इस जवाब की प्रतिलिपि बन, सुधार किया गया है के बाद से इस उत्तर पोस्ट किया गया था।
क्रिस

51

यह कार्यक्षमता तृतीय-पक्ष पैकेज से उपलब्ध है ।

pip install djangorestframework-queryfields

अपने धारावाहिक को इस तरह घोषित करें:

from rest_framework.serializers import ModelSerializer
from drf_queryfields import QueryFieldsMixin

class MyModelSerializer(QueryFieldsMixin, ModelSerializer):
    ...

तब फ़ील्ड अब क्वेरी तर्क का उपयोग करके निर्दिष्ट किया जा सकता है (क्लाइंट-साइड):

GET /identities/?fields=id,data

बहिष्करण फ़िल्टरिंग भी संभव है, उदाहरण के लिए आईडी को छोड़कर हर क्षेत्र को वापस करने के लिए :

GET /identities/?fields!=id

अस्वीकरण: मैं लेखक / अनुरक्षक हूँ।


1
नमस्ते। इस और github.com/dbrgn/drf-dynamic-fields के बीच अंतर क्या है (चुने हुए उत्तर की टिप्पणियों में लिंक किया गया है)?
डैनिलो बार्गेन

5
धन्यवाद, मुझे उस कार्यान्वयन पर एक नज़र थी, और ऐसा लगता है कि यह एक ही मूल विचार है। लेकिन dbrgnकार्यान्वयन में कुछ अंतर हैं: 1. के साथ समर्थन नहीं करता है fields!=key1,key2। 2. GET अनुरोध संदर्भ के बाहर के धारावाहिक को भी संशोधित करता है, जो कुछ PUT / POST अनुरोधों को तोड़ सकता है। 3. उदाहरण के लिए fields=key1&fields=key2, जो ajax एप्लिकेशन के लिए एक अच्छा-से-है के साथ फ़ील्ड जमा नहीं करता है । इसमें शून्य परीक्षण कवरेज भी है, जो ओएसएस में कुछ हद तक असामान्य है।
विम

1
@wim DRF और Django के कौन से संस्करण आपके पुस्तकालय का समर्थन करते हैं? मुझे डॉक्स में कुछ भी नहीं मिला।
पावेल्स्वेकी

1
Django 1.7-1.11 +, मूल रूप से कोई भी कॉन्फ़िगरेशन जो DRF का समर्थन करता है। यह टिप्पणी पुरानी हो सकती है, इसलिए सीआई के लिए परीक्षण मैट्रिक्स की जाँच करें , यहाँ
wim

1
मेरे लिए बहुत अच्छा काम करता है: Django == 2.2.7, djangorestframework == 3.10.3, djangorestframework-queryfields == 1.0.0
नीरज कश्यप

7

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

3

एक नया पृष्ठ पर अंक लगाना सीरियल क्लास को कॉन्फ़िगर करें

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):
    # [...]

निवेदन

अब, जब आप किसी संसाधन का अनुरोध करते हैं, तो आप fieldsurl में केवल निर्दिष्ट फ़ील्ड दिखाने के लिए एक पैरामीटर जोड़ सकते हैं । /?fields=field1,field2

आप यहां एक अनुस्मारक पा सकते हैं: https://gist.github.com/Kmaschta/e28cf21fb3f0b90c597a


2

आप डायनेमिक रीस्ट की कोशिश कर सकते हैं , जिसमें डायनेमिक फील्ड्स (इंक्लूजन, एक्सक्लूजन) के लिए सपोर्ट है, एंबेडेड / साइडलोड किए गए ऑब्जेक्ट्स, फिल्टरिंग, ऑर्डरिंग, पेजिनेशन और बहुत कुछ।


1

ऐसी कार्यक्षमता जो हमने drf_tweaks / control-over-serialized-field में प्रदान की है ।

यदि आप हमारे धारावाहिकों का उपयोग करते हैं, तो आपको ?fields=x,y,zकेवल क्वेरी में पैरामीटर पास करना होगा ।


1

नेस्टेड डेटा के लिए, मैं डॉक्स , ड्रफ-फ्लेक्सफ़िल्ड्स में अनुशंसित पैकेज के साथ Django रेस्ट फ्रेमवर्क का उपयोग कर रहा हूं

यह आपको माता-पिता और बच्चे दोनों वस्तुओं पर लौटाए गए क्षेत्रों को प्रतिबंधित करने की अनुमति देता है। रेडमी में निर्देश अच्छे हैं, बस कुछ चीजों को देखने के लिए:

URL की जरूरत है जैसे / इस '/ व्यक्ति / /? विस्तार = देश और फ़ील्ड = आईडी, नाम, देश' के बजाय readme में लिखा है / व्यक्ति? विस्तार = देश और क्षेत्रों = आईडी, नाम, देश '

नेस्टेड ऑब्जेक्ट का नामकरण और उससे संबंधित नाम पूरी तरह से सुसंगत होना चाहिए, जो अन्यथा आवश्यक नहीं है।

यदि आपके पास 'कई' हैं, जैसे कि एक देश में कई राज्य हो सकते हैं, तो आपको 'कई' सेट करने की आवश्यकता होगी: डॉक्स में वर्णित के अनुसार सीरियलाइज़र में सही है।


1

यदि आप ग्राफ़िकल जैसे कुछ लचीला चाहते हैं, तो आप 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"
            }
        ]
      },
      ...
    ]
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.