आप Django रेस्ट फ्रेमवर्क में एक नेस्टेड सीरीज़र को कैसे फ़िल्टर करते हैं?


84

Django रेस्ट फ्रेमवर्क में, जब आप किसी अन्य धारावाहिक में नेस्टेड होते हैं, तो आप एक धारावाहिक को कैसे फ़िल्टर करते हैं?

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

मैंने ओरिजिनल व्यूसेट पर एक फ़िल्टर जोड़ने की कोशिश की है, लेकिन यह नेस्टेड परिणामों को फ़िल्टर नहीं करता है क्योंकि नेस्टेड परिणाम को एक अलग प्री-फ्रेटेड क्वेरी के रूप में कहा जाता है। (नेस्टेड धारावाहिक एक रिवर्स लुकअप है, आप देखते हैं।)

क्या नेस्टेड सीरीज़र में एक get_queryset () ओवरराइड को जोड़ना संभव है (इसे व्यूसेट से बाहर ले जाना), वहां फ़िल्टर को जोड़ना? मैंने कोशिश की है, भी, कोई भाग्य के साथ।

यह वही है जो मैंने कोशिश की, लेकिन यह भी कहा जाता प्रतीत नहीं होता है:

class QuestionnaireSerializer(serializers.ModelSerializer):
    edition = EditionSerializer(read_only=True)
    company = serializers.StringRelatedField(read_only=True)

    class Meta:
        model = Questionnaire

    def get_queryset(self):
        query = super(QuestionnaireSerializer, self).get_queryset(instance)
        if not self.request.user.is_staff:
            query = query.filter(user=self.request.user, edition__hide=False)
        return query

6
get_querysetएक वर्ग पर है ModelViewSet, सीरियल पर नहीं, यही कारण है कि यह नहीं कहा जा रहा है
NotSimon

जवाबों:


99

आप ListSerializer को उप-वर्ग कर सकते हैं और to_representationविधि को अधिलेखित कर सकते हैं ।

डिफ़ॉल्ट रूप से to_representationविधि data.all()नेस्टेड क्वेरीसेट पर कॉल करती है। तो आपको data = data.filter(**your_filters)विधि को कहने से पहले प्रभावी ढंग से बनाने की आवश्यकता है । फिर आपको अपने उप-सूचीबद्ध लिस्टसियराइज़र को nested serializer के मेटा पर list_serializer_class के रूप में जोड़ना होगा।

  1. उपवर्ग सूची सूची, अधिलेखित to_representationऔर फिर सुपर बुला रहा है
  2. list_serializer_classनेस्टेड सीरीज़लाइज़र पर मेटा के रूप में उप-सूचीबद्ध लिस्ट सेरिज़ाइज़र जोड़ें

यहाँ आपके नमूने के लिए प्रासंगिक कोड है।

class FilteredListSerializer(serializers.ListSerializer):

    def to_representation(self, data):
        data = data.filter(user=self.context['request'].user, edition__hide=False)
        return super(FilteredListSerializer, self).to_representation(data)


class EditionSerializer(serializers.ModelSerializer):

    class Meta:
        list_serializer_class = FilteredListSerializer
        model = Edition


class QuestionnaireSerializer(serializers.ModelSerializer):
    edition = EditionSerializer(read_only=True)
    company = serializers.StringRelatedField(read_only=True)

    class Meta:
        model = Questionnaire

1
यही किया चाल! हालांकि अंत में मैंने तय किया कि मेरे धारावाहिक बहुत जटिल हो रहे हैं और मैंने उन सभी को फिर से तैयार कर लिया, जिससे ग्राहक को कुछ और एपीआई कॉल चलाने के लिए मजबूर होना पड़ा, लेकिन मेरे ऐप को बहुत सरल बना दिया।
जॉन

3
एक समान समस्या के समाधान के लिए एक आधार के रूप में इसका उपयोग करने की कोशिश करना; सुनिश्चित नहीं है कि क्या यह वास्तव में अपने प्रश्न का गुण है। मैं QuestionnaireSerializerListSerializer में एक var कैसे पास करूंगा? अनुमानित करने के लिए, मुझे एडिशन की आईडी के साथ-साथ प्रश्नावली की आईडी को भी फ़िल्टर करना होगा।
ब्रेंडन

3
यह डीआरएफ प्रलेखन में होना चाहिए। सुपर उपयोगी धन्यवाद!
डैनियल वैन फ्लायमैन

7
मेरे कार्यान्वयन में, मुझे 'FilteredListSerializer' object has no attribute 'request'कोई और मिल रहा है?
डोमिनोज

11
@Dominooch उत्तर देने के लिए आप उपयोग self.context [ 'अनुरोध'] के बजाय self.request करने की जरूरत है
rojoca

25

एसओ और अन्य स्थानों से कई समाधानों का परीक्षण किया।

Django 2.0 + DRF 3.7.7 के लिए केवल एक कार्यशील समाधान मिला।

मॉडल में एक विधि को परिभाषित करें जिसमें नेस्टेड क्लास है। एक ऐसा फिल्टर क्राफ्ट करें जो आपकी आवश्यकताओं के अनुरूप हो।

class Channel(models.Model):
    name = models.CharField(max_length=40)
    number = models.IntegerField(unique=True)
    active = models.BooleanField(default=True)

    def current_epg(self):
        return Epg.objects.filter(channel=self, end__gt=datetime.now()).order_by("end")[:6]


class Epg(models.Model):
    start = models.DateTimeField()
    end = models.DateTimeField(db_index=True)
    title = models.CharField(max_length=300)
    description = models.CharField(max_length=800)
    channel = models.ForeignKey(Channel, related_name='onair', on_delete=models.CASCADE)

class EpgSerializer(serializers.ModelSerializer):
    class Meta:
        model = Epg
        fields = ('channel', 'start', 'end', 'title', 'description',)


class ChannelSerializer(serializers.ModelSerializer):
    onair = EpgSerializer(many=True, read_only=True, source="current_epg")

    class Meta:
        model = Channel
        fields = ('number', 'name', 'onair',)

ध्यान दीजिए source="current_epg"और आपको बात मिल जाएगी।


हाँ! यह टिप्पणी एक फ़ंक्शन होने के लिए स्रोत की क्षमता का लाभ उठाती है, जिसे आप मॉडल पर परिभाषित करते हैं, फिर आप वहां फ़िल्टर करने का लाभ उठा सकते हैं! ठंडा!
कब्जे

क्या कक्षा के तहत कार्य करने के लिए एक स्ट्रिंग पास करना संभव है?
एलेक्सडब्ल्यू

मुझे केवल एक कईटॉमनी संबंधित क्षेत्र के आदेश की आवश्यकता थी। कई अलग-अलग समाधानों के लिए कई प्रयास किए गए (सजा का इरादा)। लेकिन यह एकमात्र समाधान था जो मेरे लिए काम करता था! धन्यवाद!
गबन

ऐसा लगता है कि स्वीकृत उत्तर की तुलना में django के कोड दर्शन के संदर्भ में यह अधिक सही समाधान है। Django ने ActiveModel ("वसा मॉडल") दृष्टिकोण प्रस्तावित किया है, इसलिए फ़िल्टरिंग को मॉडल स्तर (या व्यूसेट स्तर) पर बनाया जाना चाहिए और क्रमांकन को व्यावसायिक तर्क के बारे में कुछ भी नहीं पता होना चाहिए।
ऑक्सफैन

14

जबकि उपरोक्त सभी उत्तर काम करते हैं, मुझे Django की Prefetchवस्तु का उपयोग सबसे आसान तरीका लगता है।

मान लें कि एक Restaurantओब्ज में बहुत से MenuItemएस हैं, जिनमें से कुछ हैं is_remove == True, और आप केवल उन लोगों को चाहते हैं जिन्हें हटाया नहीं गया है।

में RestaurantViewSet, कुछ करना पसंद है

from django.db.models import Prefetch

queryset = Restaurant.objects.prefetch_related(
    Prefetch('menu_items', queryset=MenuItem.objects.filter(is_removed=False), to_attr='filtered_menu_items')
)

में RestaurantSerializer, कुछ करना पसंद है

class RestaurantSerializer(serializers.ModelSerializer):
    menu_items = MenuItemSerializer(source='filtered_menu_items', many=True, read_only=True)


2
महान समाधान, मैं मानता हूं कि इसे हल करने का सबसे अच्छा तरीका है।
जॉर्डन

यह सबसे ऊपर होना चाहिए। वर्तमान शीर्ष समाधान डेटाबेस से पहले से ही निकाले जाने के बाद डेटा को_प्रदर्शन के साथ फ़िल्टर करता है। यह समाधान क्वेरी पर डेटा को फ़िल्टर करता है और इसे एक थोक अनुरोध में लाता है। ज्यादातर मामलों के लिए बेहतर तरीका है।
एलेक्स

इससे आपको बहुत मदद मिली, धन्यवाद!
शाम

7

जब एक धारावाहिक को त्वरित किया जाता है और कई = सत्य पारित हो जाते हैं, तो एक सूची सूची का निर्माण किया जाएगा। धारावाहिक वर्ग तब माता-पिता की सूची का एक बच्चा बन जाता है

यह विधि फ़ील्ड के लक्ष्य को मान तर्क के रूप में लेती है, और उस प्रतिनिधित्व को वापस करना चाहिए जिसका उपयोग लक्ष्य को क्रमबद्ध करने के लिए किया जाना चाहिए। मान तर्क आमतौर पर एक मॉडल उदाहरण होगा।

नीचे नेस्टेड सीरियलाइज़र का उदाहरण है

class UserSerializer(serializers.ModelSerializer):
    """ Here many=True is passed, So a ListSerializer instance will be 
     created"""
    system = SystemSerializer(many=True, read_only=True)

    class Meta:
        model = UserProfile
        fields = ('system', 'name')

class FilteredListSerializer(serializers.ListSerializer):
    
    """Serializer to filter the active system, which is a boolen field in 
       System Model. The value argument to to_representation() method is 
      the model instance"""
    
    def to_representation(self, data):
        data = data.filter(system_active=True)
        return super(FilteredListSerializer, self).to_representation(data)

class SystemSerializer(serializers.ModelSerializer):
    mac_id = serializers.CharField(source='id')
    system_name = serializers.CharField(source='name')
    serial_number = serializers.CharField(source='serial')

    class Meta:
        model = System
        list_serializer_class = FilteredListSerializer
        fields = (
            'mac_id', 'serial_number', 'system_name', 'system_active', 
        )

दृश्य में:

class SystemView(viewsets.GenericViewSet, viewsets.ViewSet):
    def retrieve(self, request, email=None):
        data = get_object_or_404(UserProfile.objects.all(), email=email)
        serializer = UserSerializer(data)
        return Response(serializer.data)

5

मुझे यह आसान लगता है, और अधिक सीधे आगे, SerializerMethodFieldधारावाहिक क्षेत्र पर उपयोग करने के लिए जिसे आप फ़िल्टर करना चाहते हैं।

तो आप कुछ इस तरह से करेंगे।

class CarTypesSerializer(serializers.ModelSerializer):

    class Meta:
        model = CarType
        fields = '__all__'


class CarSerializer(serializers.ModelSerializer):

    car_types = serializers.SerializerMethodField()

    class Meta:
        model = Car
        fields = '__all__'

    def get_car_types(self, instance):
        # Filter using the Car model instance and the CarType's related_name
        # (which in this case defaults to car_types_set)
        car_types_instances = instance.car_types_set.filter(brand="Toyota")
        return CarTypesSerializer(car_types_instances, many=True).data

यह आपको serializers.ListSerializerअलग-अलग धारावाहिकों के लिए अलग-अलग फ़िल्टरिंग मानदंडों की आवश्यकता होने पर कई ओवरराइड बनाने से बचाता है ।

यह भी देखने का अतिरिक्त लाभ है कि फिल्टर एक सबक्लास परिभाषा में गोता लगाने के बजाय धारावाहिक के भीतर क्या करता है।

बेशक नकारात्मक पक्ष यह है कि यदि आपके पास कई नेस्टेड ऑब्जेक्ट्स के साथ एक धारावाहिक है जो सभी को किसी तरह से फ़िल्टर करने की आवश्यकता है। यह सीरियल कोड को बहुत बढ़ा सकता है। यह आपके ऊपर है कि आप कैसे फ़िल्टर करना चाहते हैं।

उम्मीद है की यह मदद करेगा!

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.