django-rest-framework 3.0 im verschachtelten serializer erstellen oder aktualisieren

72

Mit Django-Rest-Framework 3.0 und diesen einfachen Modellen:

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)

Und angesichts dieser JSON-Anfrage:

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

Wie kann ich einen verschachtelten Serializer schreiben, um diesen JSON zu verarbeiten, und für jeden pagefür den angegebenen bookentweder eine neue Seite erstellen oder aktualisieren, falls vorhanden.

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


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

Ich weiß, dass das Instanziieren des Serializers mit einem instanceden aktuellen aktualisiert, aber wie soll ich ihn innerhalb der createMethode des verschachtelten Serializers verwenden?

Sam R.
quelle

Antworten:

105

Möchten Sie zunächst das Erstellen neuer Buchinstanzen oder nur das Aktualisieren vorhandener unterstützen?

Wenn Sie nur neue Buchinstanzen erstellen wollten, könnten Sie so etwas tun ...

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

Beachten Sie, dass ich das hier nicht aufgenommen book_idhabe. Wenn wir Buchinstanzen erstellen, wird keine Buch-ID angegeben. Wenn wir Buchinstanzen aktualisieren, fügen wir normalerweise die Buch-ID als Teil der URL und nicht in die Anforderungsdaten ein.

Wenn Sie beide unterstützen möchten erstellen und zu aktualisieren Buch Instanzen dann müssen Sie darüber nachdenken , wie Sie Seiten behandeln , die nicht im Antrag enthalten sind, aber sind derzeit mit dem Buch - Instanz zugeordnet.

Sie können diese Seiten stillschweigend ignorieren und so lassen, wie sie sind. Möglicherweise möchten Sie einen Validierungsfehler auslösen oder sie löschen.

Angenommen, Sie möchten alle Seiten löschen, die nicht in der Anforderung enthalten sind.

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

Es ist auch möglich, dass Sie nur Buchaktualisierungen und nicht die Erstellung unterstützen möchten. In diesem Fall sollten Sie nur die update()Methode einbeziehen.

Es gibt auch verschiedene Möglichkeiten, wie Sie die Anzahl der Abfragen reduzieren können, z. Verwenden von Massenerstellung / -löschung, aber das oben Genannte würde die Arbeit auf ziemlich einfache Weise erledigen.

Wie Sie sehen, gibt es Feinheiten in den Verhaltensweisen, die Sie beim Umgang mit verschachtelten Daten möglicherweise wünschen. Überlegen Sie sich daher genau, welches Verhalten Sie in verschiedenen Fällen erwarten.

Beachten Sie auch, dass ich Serializerim obigen Beispiel eher als verwendet habe ModelSerializer. In diesem Fall ist es einfacher, alle Felder explizit in die Serializer-Klasse aufzunehmen, als sich auf den automatischen Satz von Feldern zu verlassen, ModelSerializerder standardmäßig generiert wird.

Tom Christie
quelle
1
you might want to only support book updates ... , only include the update() method. Wie wird instancein diesem Fall die In-Update-Methode mit einem vorhandenen Buch gefüllt?
Sam R.
1
Danke Tom. Ich hab es jetzt.
Sam R.
2
Warum befindet sich die Überschreibung im Serializer und nicht in der Ansicht, wie z. B .: Django-rest-framework.org/api-guide/viewsets/… ?
Billig AKA Ju
3
@ TomChristie Könnten Sie mir den Gefallen tun und einen Blick auf meinen Versuch werfen , verschachtelte Ressourcen zu erstellen ? Ich habe keine Ideen mehr, nachdem ich Django-Rest-Framework-Nested-Resource , DRF-Erweiterungen und DrF-Nested-Router ausprobiert habe - alles ohne Erfolg. Ich bin froh, zu etwas zu wechseln, das tatsächlich funktioniert.
JJD
2
@ TomChristie Wenn ich ModelSerializer anstelle von Serializer verwende, wird es herausgefiltert page_id.
Sassan
3

Sie können einfach drf-writable-nested verwenden . Dadurch werden Ihre verschachtelten Serialisierer automatisch beschreibbar und aktualisierbar.

in dir serializers.py:

from drf_writable_nested import WritableNestedModelSerializer

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


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

Und das ist es!

Außerdem unterstützt die Bibliothek die Verwendung nur einer der createund update-Logiken, wenn Sie nicht beide benötigen.

Peter Sobhi
quelle
Wie kann man eine Nestet-Entität nur nach ID erstellen und bearbeiten?
Alex78191
In meinem Fall ist der Anser stackoverflow.com/questions/41394761/…
Alex78191