1. ホーム
  2. python

django-rest-framework 3.0 ネストされたシリアライザーで作成または更新する

2023-09-27 16:11:56

質問

django-rest-framework 3.0を使用しています。 で、これらの単純なモデルを持っています。

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)

そして、このJSONのリクエストを与えると

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

このJSONを処理するネストされたシリアライザーをどのように書けばよいのでしょうか? page に対して、与えられた book は、新しいページを作成するか、存在する場合は更新します。

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


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

シリアライザーをインスタンス化する際に instance でインスタンス化すると現在のものが更新されることは知っていますが、どのようにして create メソッドの中でどのように使えばよいのでしょうか?

どのように解決するのですか?

まず、新しいブックインスタンスの作成をサポートしたいのか、それとも既存のブックインスタンスの更新のみをサポートしたいのか。

もし、新しいブックインスタンスを作成したいだけなら、次のようなことができます。

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

なお、私は はしていません。 を含んでいます。 book_id を追加していません。ブックインスタンスを作成するときには、ブックIDを含めることはありません。ブックインスタンスを更新するときは、リクエストデータではなくURLの一部としてブックIDを含めるのが一般的です。

ブックインスタンスの作成と更新の両方をサポートしたい場合、リクエストに含まれないページをどのように処理したいかを考える必要があります。 はブックインスタンスに現在関連付けられています。

これらのページを黙って無視してそのままにしておくこともできますし、検証エラーを発生させることもできますし、削除することもできます。

リクエストに含まれていないすべてのページを削除したいと仮定しましょう。

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

また、以下のようなことも考えられます。 だけ はブックの更新をサポートし、作成はサポートしない、ということもあり得ますが、その場合は だけ を含めるようにします。 update() メソッドを含んでいます。

また、一括作成/削除を使用するなど、クエリの数を減らすさまざまな方法がありますが、上記の方法はかなり簡単な方法で仕事をこなすでしょう。

お分かりのように、ネストされたデータを処理する際に必要となる動作の種類には微妙なものがあります。したがって、さまざまなケースでどのような動作を期待しているのか、正確によく考えてみてください。

また、注意点として、私は Serializer ではなく、上記の例では ModelSerializer . この場合、シリアライザークラスにすべてのフィールドを明示的に含める方が簡単です。 ModelSerializer がデフォルトで生成するフィールドの自動セットに頼るのではなく、シリアライザークラスにすべてのフィールドを明示的に含めるだけです。