web-dev-qa-db-fra.com

Django-Rest-Framework 3.0 créer ou mettre à jour dans sérialiseur imbriqué

Avec Django-rest-framework 3. et ayant ces modèles simples:

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)

Et étant donné cette requête JSON:

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

Comment puis-je écrire un sérialiseur imbriqué pour traiter ce JSON et pour chaque page pour le book donné créer une nouvelle page ou mettre à jour s'il existe.

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


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

Je sais que l'instanciation du sérialiseur avec un instance mettra à jour celui en cours, mais comment dois-je l'utiliser dans la méthode create du sérialiseur imbriqué?

70
norbertpy

Tout d'abord, souhaitez-vous soutenir la création de nouvelles instances de livre ou uniquement la mise à jour des instances existantes?

Si vous ne vouliez que créer de nouvelles instances de livre, vous pourriez faire quelque chose comme ça ...

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

Notez que je n'ai pas inclus le book_id Ici. Lorsque nous créons des occurrences de livre, nous n'incluons pas d'identifiant de livre. Lorsque nous mettons à jour des instances de livre, nous incluons généralement l'ID de livre dans l'URL, plutôt que dans les données de la demande.

Si vous souhaitez prendre en charge à la fois la création et la mise à jour d'instances de livre, vous devez réfléchir à la façon dont vous souhaitez gérer les pages qui ne sont pas incluses dans la demande, mais dont sont actuellement associé à l'instance de livre.

Vous pouvez choisir d'ignorer ces pages en silence et de les laisser telles quelles, vous pouvez également générer une erreur de validation ou les supprimer.

Supposons que vous souhaitiez supprimer les pages non incluses dans la demande.

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

Il est également possible que vous souhaitiez uniquement prendre en charge les mises à jour de livre, et non la création, auquel cas, uniquement inclut la méthode update().

Il existe également différentes façons de réduire le nombre de requêtes, par exemple. en utilisant la création/suppression en bloc, mais ce qui précède ferait le travail de manière assez simple.

Comme vous pouvez le constater, les types de comportement que vous souhaitez obtenir lorsque vous manipulez des données imbriquées sont subtils. Réfléchissez donc bien au comportement que vous attendez dans différents cas.

Notez également que j'ai utilisé Serializer dans l'exemple ci-dessus plutôt que ModelSerializer. Dans ce cas, il est plus simple d'inclure explicitement tous les champs de la classe de sérialiseur, plutôt que de s'appuyer sur le jeu automatique de champs que ModelSerializer génère par défaut.

95
Tom Christie

Vous pouvez simplement utiliser drf-writeable-nested . Cela rend automatiquement vos sérialiseurs imbriqués en écriture et modifiables.

en toi 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

Et c'est tout!

De plus, la bibliothèque ne prend en charge que l’une des logiques create et update si vous n’avez pas besoin des deux.

0
Peter Sobhi