2016-07-11 7 views
5

इस मामले पर विचार करें जहां मेरे पास Book और Author मॉडल है।अनुरोध प्रकार के आधार पर एक Django REST Framework ModelSerializer में एक फ़ील्ड बदलें?

serializers.py

class AuthorSerializer(serializers.ModelSerializer): 

    class Meta: 
     model = models.Author 
     fields = ('id', 'name') 

class BookSerializer(serializers.ModelSerializer): 
    author = AuthorSerializer(read_only=True) 

    class Meta: 
     model = models.Book 
     fields = ('id', 'title', 'author') 

viewsets.py

class BookViewSet(viewsets.ModelViewSet): 
    queryset = Book.objects.all() 
    serializer_class = BookSerializer 

यह अच्छा काम करता है अगर मैं एक पुस्तक के लिए एक GET अनुरोध भेजें। मुझे एक नेस्टेड धारावाहिक के साथ एक आउटपुट मिलता है जिसमें पुस्तक विवरण और घोंसला वाले लेखक विवरण होते हैं, जो मैं चाहता हूं।

हालांकि, जब मैं बनाने के लिए/एक पुस्तक अद्यतन चाहते हैं, मैं एक POST/PUT/सिर्फ अपने आईडी के बजाय लेखक की नेस्टेड विवरण के साथ PATCH भेज दिया। मैं लेखक आईडी निर्दिष्ट करके पुस्तक ऑब्जेक्ट को बनाने/अपडेट करने में सक्षम होना चाहता हूं, न कि संपूर्ण लेखक ऑब्जेक्ट।

तो, कुछ जहाँ मेरे serializer एक GET अनुरोध

class BookSerializer(serializers.ModelSerializer): 
    author = AuthorSerializer(read_only=True) 

    class Meta: 
     model = models.Book 
     fields = ('id', 'title', 'author') 

के लिए इस तरह दिखता है और मेरी serializer एक POST, PUT, PATCH अनुरोध

class BookSerializer(serializers.ModelSerializer): 
    author = PrimaryKeyRelatedField(queryset=Author.objects.all()) 

    class Meta: 
     model = models.Book 
     fields = ('id', 'title', 'author') 

मैं भी नहीं चाहते हैं के लिए इस तरह दिखता है प्रत्येक प्रकार के अनुरोध के लिए दो पूरी तरह से अलग धारावाहिक बनाने के लिए। मैं फ़ील्ड को BookSerializer में बस संशोधित करना चाहता हूं।

आखिरकार, क्या यह पूरी बात करने का एक बेहतर तरीका है?

+0

http://www.django-rest-framework.org/api-guide/routers/ पर देखें - अपनी आवश्यकताओं के साथ पत्राचार में सजावटी जोड़ें। – dmitryro

+0

@dmitryro मुझे समझ में नहीं आता है। क्या आप आगे बता सकते हैं? सजावटकर्ताओं को धारावाहिकों के लिए क्षेत्रों को कैसे संशोधित किया जाएगा? –

+0

आपको एक कस्टम राउटर बनाना होगा जो विभिन्न अनुरोध विधियों को प्रबंधित करेगा - पोस्ट, प्राप्त करें, पुट करें, और अपनी विधियों को सजाने के लिए जो अनुरोध विधि आप उपयोग करना चाहते हैं - दस्तावेज़ीकरण कुछ नमूने प्रदान करता है। यह भी देखें http://stackoverflow.com/questions/28957912/overriding-django-rest-viewset-with-custom-post-method-and-model – dmitryro

उत्तर

5

आईएमएचओ, एकाधिक धारावाहिक केवल अधिक से अधिक भ्रम पैदा करने जा रहे हैं।

बल्कि मैं समाधान नीचे पसंद करेंगे:

  1. अपने viewset परिवर्तित न करें (यह डिफ़ॉल्ट छोड़ दें)
  2. अपने serializer में .validate() विधि जोड़ें; अन्य आवश्यक .create या .update() इत्यादि के साथ, वास्तविक तर्क मान्य() विधि में जाएगा। अनुरोध प्रकार के आधार पर हम हमारे serializer द्वारा आवश्यक मान्य_डेटा निर्देश बनायेंगे।

मुझे लगता है कि यह सबसे साफ दृष्टिकोण है।

DRF: Allow all fields in GET request but restrict POST to just one field

+0

यह इस समस्या का सबसे सुंदर समाधान है! – Ajoy

0

जिस तरह से मैंने इस समस्या से निपटने का अंत किया था, वह एक संबंधित क्षेत्र के लिए एक और धारावाहिक था।

class HumanSerializer(PersonSerializer): 

    class Meta: 
     model = Human 
     fields = PersonSerializer.Meta.fields + (
      'firstname', 
      'middlename', 
      'lastname', 
      'sex', 
      'date_of_birth', 
      'balance' 
     ) 
     read_only_fields = ('name',) 


class HumanRelatedSerializer(HumanSerializer): 
    def to_internal_value(self, data): 
     return self.Meta.model.objects.get(id=data['id']) 


class PhoneNumberSerializer(serializers.ModelSerializer): 
    contact = HumanRelatedSerializer() 

    class Meta: 
     model = PhoneNumber 
     fields = (
      'id', 
      'contact', 
      'phone', 
      'extension' 
     ) 

आप कुछ इस तरह कर सकता है, लेकिन RelatedSerializer के लिए कार्य करें:

def to_internal_value(self, data): 
    return self.Meta.model.objects.get(id=data) 

इस प्रकार, जब serializing, आप संबंधित वस्तु को क्रमानुसार, और जब डी-serializing, आप केवल करने के लिए आईडी की जरूरत है संबंधित वस्तु प्राप्त करें।

+0

क्या यह किया गया। काम नहीं किया। क्या मैं गलत हूं? 'वर्ग AuthorRelatedSerializer (AuthorSerializer): डीईएफ़ to_internal_value (स्वयं, डेटा): वापसी self.Meta.model.objects.get (आईडी = डेटा [ 'आईडी']) वर्ग BookSerializer (serializers.ModelSerializer): लेखक = AuthorRelatedSerializer() ' –

+0

मेरा उदाहरण {id: 6, ...} की अपेक्षा करता है, यदि आप इसे केवल एक पूर्णांक पास कर रहे हैं, तो यह' def to_internal_value (स्वयं, डेटा) होना चाहिए: स्वयं को वापस करें। Meta.model। objects.get (id = data) ' – miyamoto

2

आप ViewSet पर get_serializer_class विधि की तलाश कर रहे हैं। यह आपको अनुरोध प्रकार पर स्विच करने की अनुमति देता है जिसके लिए आप उपयोग करना चाहते हैं।

from rest_framework import viewsets 

class MyModelViewSet(viewsets.ModelViewSet): 

    model = MyModel 
    queryset = MyModel.objects.all() 

    def get_serializer_class(self): 
     if self.action in ('create', 'update', 'partial_update'): 
      return MySerializerWithPrimaryKeysForCreatingOrUpdating 
     else: 
      return MySerializerWithNestedData 
+0

यह एक संभावित तरीका है जो काम कर सकता है, लेकिन मैं दो पूरी तरह से अलग धारावाहिकों को घोषित नहीं करना चाहता क्योंकि यह अनावश्यक होगा। मैं सोच रहा था कि एक धारावाहिक में ऐसा करने का बेहतर तरीका था या नहीं। –

2

पर मेरे समान समस्या और समाधान देखें जहां गतिशील serializer http://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields

मेरे उपयोग के मामले पर खेतों को बदल सकते हैं डीआरएफ की एक विशेषता है: उपयोग स्लग क्षेत्र पर मिलता है तो हम देख सकते हैं एक रिलेशनशिप का अच्छा प्रतिनिधि, लेकिन पोस्ट/पुट पर क्लासिक प्राथमिक कुंजी अपडेट पर वापस स्विच करें। संभवतः इकाई परीक्षण

class FooSerializer(serializers.ModelSerializer): 
    bar = serializers.SlugRelatedField(slug_field='baz', queryset=models.Bar.objects.all()) 

    class Meta: 
     model = models.Foo 
     fields = '__all__' 

    def __init__(self, *args, **kwargs): 
     super(FooSerializer, self).__init__(*args, **kwargs) 

     try: 
      if self.context['request'].method in ['POST', 'PUT']: 
       self.fields['bar'] = serializers.PrimaryKeyRelatedField(queryset=models.Bar.objects.all()) 
     except KeyError: 
      pass 

KeyError कभी कभी एक अनुरोध के बिना कोड initialisation पर फेंक दिया जाता है,: कुछ इस तरह करने के लिए अपने serializer समायोजित करें।

जिम्मेदारी का आनंद लें और उपयोग करें।