2014-12-11 6 views
61

Con django-resto-quadro 3.0 e avendo questi semplici modelli:django-resto-quadro 3.0 creare o aggiornare in serializzatore nidificato

class Book(models.Model): 
    title = models.CharField(max_length=50) 


class Page(models.Model): 
    book = models.ForeignKey(Books, related_name='related_book') 
    text = models.CharField(max_length=500) 

E data questa richiesta JSON:

{ 
    "book_id":1, 
    "pages":[ 
     { 
     "page_id":2, 
     "text":"loremipsum" 
     }, 
     { 
     "page_id":4, 
     "text":"loremipsum" 
     } 
    ] 
} 

Come posso scrivere un serializzatore annidato per elaborare questo JSON e per ogni page per il dato book o creare una nuova pagina o aggiornare se esiste.

class RequestSerializer(serializers.Serializer): 
    book_id = serializers.IntegerField() 
    page = PageSerializer(many=True) 


class PageSerializer(serializers.ModelSerializer): 
    class Meta: 
     model = Page 

So che istanziare la serializzatore con una instance aggiornerà quella attuale, ma come dovrei usarlo all'interno del metodo di serializzatore annidato create?

risposta

84

In primo luogo, vuoi supportare la creazione di nuove istanze di libri o solo l'aggiornamento di quelle esistenti?

Se sempre e solo voluto creare nuove istanze del libro che si potrebbe fare qualcosa di simile ...

class PageSerializer(serializers.Serializer): 
    text = serializers.CharField(max_length=500) 

class BookSerializer(serializers.Serializer): 
    page = PageSerializer(many=True) 
    title = serializers.CharField(max_length=50) 

    def create(self, validated_data): 
     # Create the book instance 
     book = Book.objects.create(title=validated_data['title']) 

     # Create or update each page instance 
     for item in validated_data['pages']: 
      page = Page(id=item['page_id'], text=item['text'], book=book) 
      page.save() 

     return book 

Nota che io non ho incluso la book_id qui. Quando creiamo istanze di libri, non includeremo l'id di un libro. Quando aggiorniamo le istanze di libri, in genere includiamo l'id libro come parte dell'URL, piuttosto che nei dati della richiesta.

Se si desidera supportare sia la creazione e l'aggiornamento delle istanze del libro, è necessario pensare a come si desidera gestire le pagine non incluse nella richiesta, ma sono attualmente associate all'istanza del libro.

È possibile scegliere di ignorare automaticamente tali pagine e lasciarle così come sono, è possibile che si desideri sollevare un errore di convalida o che si desideri eliminarle.

Supponiamo che vogliate cancellare qualsiasi pagina non inclusa nella richiesta.

def create(self, validated_data): 
    # As before. 
    ... 

def update(self, instance, validated_data): 
    # Update the book instance 
    instance.title = validated_data['title'] 
    instance.save() 

    # Delete any pages not included in the request 
    page_ids = [item['page_id'] for item in validated_data['pages']] 
    for page in instance.books: 
     if page.id not in page_ids: 
      page.delete() 

    # Create or update page instances that are in the request 
    for item in validated_data['pages']: 
     page = Page(id=item['page_id'], text=item['text'], book=instance) 
     page.save() 

    return instance 

E 'anche possibile che si potrebbe desiderare di solo aggiornamenti supporto del libro, e non sostenere la creazione, nel qual caso, solo includono il metodo update().

Esistono vari modi per ridurre il numero di query, ad es. utilizzando la creazione/eliminazione di massa, ma quanto sopra farebbe il lavoro in un modo abbastanza semplice.

Come potete vedere ci sono sottigliezze nei tipi di comportamento che potreste desiderare quando si tratta di dati annidati, quindi pensate attentamente esattamente a quale comportamento vi aspettate in vari casi.

Si noti inoltre che ho utilizzato Serializer nell'esempio sopra anziché ModelSerializer. In questo caso è più semplice includere tutti i campi nella classe serializzatore esplicitamente, piuttosto che fare affidamento sull'insieme automatico di campi che lo standard ModelSerializer genera automaticamente.

+1

'potresti voler supportare solo gli aggiornamenti dei libri ..., includere solo il metodo update()'. In questo caso, come il 'instance' nel metodo di aggiornamento sarà riempito con un libro esistente? – norbertpy

+0

Istanziando il serializzatore con l'argomento della parola chiave 'instance'. Normalmente lo otterresti eseguendo una ricerca basata su una chiave primaria nell'URL. Se stai utilizzando le visualizzazioni generiche che verranno gestite per te. Dai un'occhiata a DetailMixin in 'mixins.py' per l'implementazione di questo. –

+1

Grazie Tom. Ho capito ora. – norbertpy