relations.py

シリアライザーのリレーション

アルゴリズムではなく、データ構造がプログラミングの中心です。

ロブ・パイク

リレーショナルフィールドは、モデルのリレーションシップを表すために使用されます。ForeignKeyManyToManyFieldOneToOneField のリレーションシップだけでなく、逆リレーションシップや GenericForeignKey などのカスタムリレーションシップにも適用できます。


注: リレーショナルフィールドは relations.py で宣言されていますが、慣例として、from rest_framework import serializers を使用して serializers モジュールからインポートし、serializers.<FieldName> としてフィールドを参照する必要があります。



注: REST Framework は、select_relatedprefetch_related の点でシリアライザーに渡されるクエリセットを自動的に最適化しようとはしません。これは、過剰な魔法になるためです。ソース属性を通じてORMリレーションにまたがるフィールドを持つシリアライザーは、データベースから関連オブジェクトを取得するために追加のデータベースヒットを必要とする可能性があります。このようなシリアライザーを使用しているときに発生する可能性のある追加のデータベースヒットを回避するためにクエリを最適化するのはプログラマーの責任です。

たとえば、次のシリアライザーは、トラックフィールドがプリフェッチされていない場合、評価するたびにデータベースヒットにつながります

class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.SlugRelatedField(
        many=True,
        read_only=True,
        slug_field='title'
    )

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

# For each album object, tracks should be fetched from database
qs = Album.objects.all()
print(AlbumSerializer(qs, many=True).data)

AlbumSerializermany=True でかなり大きなクエリセットをシリアライズするために使用されている場合、深刻なパフォーマンスの問題になる可能性があります。次の方法で AlbumSerializer に渡されるクエリセットを最適化すると

qs = Album.objects.prefetch_related('tracks')
# No additional database hits required
print(AlbumSerializer(qs, many=True).data)

問題を解決します。


リレーションシップの検査。

ModelSerializer クラスを使用すると、シリアライザーフィールドとリレーションシップが自動的に生成されます。これらの自動的に生成されたフィールドを検査することは、リレーションシップスタイルをカスタマイズする方法を決定するのに役立つツールになります。

これを行うには、python manage.py shell を使用してDjangoシェルを開き、シリアライザークラスをインポートしてインスタンス化し、オブジェクト表現を出力します...

>>> from myapp.serializers import AccountSerializer
>>> serializer = AccountSerializer()
>>> print(repr(serializer))
AccountSerializer():
    id = IntegerField(label='ID', read_only=True)
    name = CharField(allow_blank=True, max_length=100, required=False)
    owner = PrimaryKeyRelatedField(queryset=User.objects.all())

APIリファレンス

さまざまなタイプのリレーショナルフィールドを説明するために、例としていくつかの単純なモデルを使用します。私たちのモデルは、音楽アルバムと、各アルバムにリストされているトラック用です。

class Album(models.Model):
    album_name = models.CharField(max_length=100)
    artist = models.CharField(max_length=100)

class Track(models.Model):
    album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE)
    order = models.IntegerField()
    title = models.CharField(max_length=100)
    duration = models.IntegerField()

    class Meta:
        unique_together = ['album', 'order']
        ordering = ['order']

    def __str__(self):
        return '%d: %s' % (self.order, self.title)

StringRelatedField

StringRelatedField は、その __str__ メソッドを使用してリレーションシップのターゲットを表すために使用できます。

たとえば、次のシリアライザー

class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.StringRelatedField(many=True)

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

は、次の表現にシリアライズされます

{
    'album_name': 'Things We Lost In The Fire',
    'artist': 'Low',
    'tracks': [
        '1: Sunflower',
        '2: Whitetail',
        '3: Dinosaur Act',
        ...
    ]
}

このフィールドは読み取り専用です。

引数:

  • many - 多対多のリレーションシップに適用する場合は、この引数を True に設定する必要があります。

PrimaryKeyRelatedField

PrimaryKeyRelatedField は、主キーを使用してリレーションシップのターゲットを表すために使用できます。

たとえば、次のシリアライザー

class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

は、このような表現にシリアライズされます

{
    'album_name': 'Undun',
    'artist': 'The Roots',
    'tracks': [
        89,
        90,
        91,
        ...
    ]
}

デフォルトでは、このフィールドは読み取り/書き込み可能ですが、read_only フラグを使用してこの動作を変更できます。

引数:

  • queryset - フィールド入力を検証する際にモデルインスタンスの検索に使用されるクエリセット。リレーションシップは、クエリセットを明示的に設定するか、read_only=True を設定する必要があります。
  • many - 多対多のリレーションシップに適用する場合は、この引数を True に設定する必要があります。
  • allow_null - True に設定すると、フィールドは null 可能リレーションシップに対して None または空の文字列の値を受け入れます。デフォルトは False です。
  • pk_field - 主キーの値のシリアライズ/デシリアライズを制御するためのフィールドに設定します。たとえば、pk_field=UUIDField(format='hex') は、UUID主キーをコンパクトな16進数表現にシリアライズします。

HyperlinkedRelatedField

HyperlinkedRelatedField は、ハイパーリンクを使用してリレーションシップのターゲットを表すために使用できます。

たとえば、次のシリアライザー

class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.HyperlinkedRelatedField(
        many=True,
        read_only=True,
        view_name='track-detail'
    )

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

は、このような表現にシリアライズされます

{
    'album_name': 'Graceland',
    'artist': 'Paul Simon',
    'tracks': [
        'http://www.example.com/api/tracks/45/',
        'http://www.example.com/api/tracks/46/',
        'http://www.example.com/api/tracks/47/',
        ...
    ]
}

デフォルトでは、このフィールドは読み取り/書き込み可能ですが、read_only フラグを使用してこの動作を変更できます。


: このフィールドは、lookup_field 引数と lookup_url_kwarg 引数を使用して設定された、単一のURLキーワード引数を受け入れるURLにマップするオブジェクト用に設計されています。

これは、URLの一部として単一の主キーまたはスラッグ引数を含むURLに適しています。

より複雑なハイパーリンク表現が必要な場合は、下のカスタムハイパーリンクフィールドセクションで説明されているように、フィールドをカスタマイズする必要があります。


引数:

  • view_name - リレーションシップのターゲットとして使用する必要があるビュー名。標準のルータークラスを使用している場合、これは <modelname>-detail という形式の文字列になります。必須
  • queryset - フィールド入力を検証する際にモデルインスタンスの検索に使用されるクエリセット。リレーションシップは、クエリセットを明示的に設定するか、read_only=True を設定する必要があります。
  • many - 多対多のリレーションシップに適用する場合は、この引数を True に設定する必要があります。
  • allow_null - True に設定すると、フィールドは null 可能リレーションシップに対して None または空の文字列の値を受け入れます。デフォルトは False です。
  • lookup_field - 参照されるビューのURLキーワード引数に対応する必要があり、検索に使用されるターゲット上のフィールド。デフォルトは 'pk' です。
  • lookup_url_kwarg - 検索フィールドに対応するURL confで定義されたキーワード引数の名前。デフォルトでは lookup_field と同じ値を使用します。
  • format - フォーマットサフィックスを使用している場合、ハイパーリンクフィールドは、format 引数を使用してオーバーライドしない限り、ターゲットに同じフォーマットサフィックスを使用します。

SlugRelatedField

SlugRelatedField は、ターゲットのフィールドを使用してリレーションシップのターゲットを表すために使用できます。

たとえば、次のシリアライザー

class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.SlugRelatedField(
        many=True,
        read_only=True,
        slug_field='title'
     )

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

は、このような表現にシリアライズされます

{
    'album_name': 'Dear John',
    'artist': 'Loney Dear',
    'tracks': [
        'Airport Surroundings',
        'Everything Turns to You',
        'I Was Only Going Out',
        ...
    ]
}

デフォルトでは、このフィールドは読み取り/書き込み可能ですが、read_only フラグを使用してこの動作を変更できます。

SlugRelatedField を読み取り/書き込みフィールドとして使用する場合、通常、スラッグフィールドが unique=True のモデルフィールドに対応していることを確認する必要があります。

引数:

  • slug_field - ターゲットを表すために使用する必要があるターゲット上のフィールド。これは、特定のインスタンスを一意に識別するフィールドである必要があります。たとえば、username です。必須
  • queryset - フィールド入力を検証する際にモデルインスタンスの検索に使用されるクエリセット。リレーションシップは、クエリセットを明示的に設定するか、read_only=True を設定する必要があります。
  • many - 多対多のリレーションシップに適用する場合は、この引数を True に設定する必要があります。
  • allow_null - True に設定すると、フィールドは null 可能リレーションシップに対して None または空の文字列の値を受け入れます。デフォルトは False です。

HyperlinkedIdentityField

このフィールドは、HyperlinkedModelSerializer の 'url' フィールドのような ID リレーションシップとして適用できます。オブジェクトの属性にも使用できます。たとえば、次のシリアライザー

class AlbumSerializer(serializers.HyperlinkedModelSerializer):
    track_listing = serializers.HyperlinkedIdentityField(view_name='track-list')

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'track_listing']

は、このような表現にシリアライズされます

{
    'album_name': 'The Eraser',
    'artist': 'Thom Yorke',
    'track_listing': 'http://www.example.com/api/track_list/12/',
}

このフィールドは常に読み取り専用です。

引数:

  • view_name - リレーションシップのターゲットとして使用する必要があるビュー名。標準のルータークラスを使用している場合、これは <model_name>-detail という形式の文字列になります。必須
  • lookup_field - 参照されるビューのURLキーワード引数に対応する必要があり、検索に使用されるターゲット上のフィールド。デフォルトは 'pk' です。
  • lookup_url_kwarg - 検索フィールドに対応するURL confで定義されたキーワード引数の名前。デフォルトでは lookup_field と同じ値を使用します。
  • format - フォーマットサフィックスを使用している場合、ハイパーリンクフィールドは、format 引数を使用してオーバーライドしない限り、ターゲットに同じフォーマットサフィックスを使用します。

ネストされたリレーションシップ

以前に説明した別のエンティティへの参照とは対照的に、参照されるエンティティは、それを参照するオブジェクトの表現に埋め込まれたり、ネストされたりすることもあります。このようなネストされたリレーションシップは、シリアライザーをフィールドとして使用することで表現できます。

フィールドを多対多のリレーションシップを表すために使用する場合は、シリアライザーフィールドに many=True フラグを追加する必要があります。

たとえば、次のシリアライザー

class TrackSerializer(serializers.ModelSerializer):
    class Meta:
        model = Track
        fields = ['order', 'title', 'duration']

class AlbumSerializer(serializers.ModelSerializer):
    tracks = TrackSerializer(many=True, read_only=True)

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

は、次のようなネストされた表現にシリアライズされます

>>> album = Album.objects.create(album_name="The Grey Album", artist='Danger Mouse')
>>> Track.objects.create(album=album, order=1, title='Public Service Announcement', duration=245)
<Track: Track object>
>>> Track.objects.create(album=album, order=2, title='What More Can I Say', duration=264)
<Track: Track object>
>>> Track.objects.create(album=album, order=3, title='Encore', duration=159)
<Track: Track object>
>>> serializer = AlbumSerializer(instance=album)
>>> serializer.data
{
    'album_name': 'The Grey Album',
    'artist': 'Danger Mouse',
    'tracks': [
        {'order': 1, 'title': 'Public Service Announcement', 'duration': 245},
        {'order': 2, 'title': 'What More Can I Say', 'duration': 264},
        {'order': 3, 'title': 'Encore', 'duration': 159},
        ...
    ],
}

書き込み可能なネストされたシリアライザー

デフォルトでは、ネストされたシリアライザーは読み取り専用です。ネストされたシリアライザーフィールドへの書き込み操作をサポートする場合は、子のリレーションシップをどのように保存する必要があるかを明示的に指定するために、create() メソッドや update() メソッドを作成する必要があります

class TrackSerializer(serializers.ModelSerializer):
    class Meta:
        model = Track
        fields = ['order', 'title', 'duration']

class AlbumSerializer(serializers.ModelSerializer):
    tracks = TrackSerializer(many=True)

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

    def create(self, validated_data):
        tracks_data = validated_data.pop('tracks')
        album = Album.objects.create(**validated_data)
        for track_data in tracks_data:
            Track.objects.create(album=album, **track_data)
        return album

>>> data = {
    'album_name': 'The Grey Album',
    'artist': 'Danger Mouse',
    'tracks': [
        {'order': 1, 'title': 'Public Service Announcement', 'duration': 245},
        {'order': 2, 'title': 'What More Can I Say', 'duration': 264},
        {'order': 3, 'title': 'Encore', 'duration': 159},
    ],
}
>>> serializer = AlbumSerializer(data=data)
>>> serializer.is_valid()
True
>>> serializer.save()
<Album: Album object>

カスタムリレーショナルフィールド

既存のリレーショナルスタイルが、必要な表現に適合しないまれなケースでは、モデルインスタンスから出力表現を生成する方法を正確に記述する、完全にカスタムのリレーショナルフィールドを実装できます。

カスタムリレーショナルフィールドを実装するには、RelatedField をオーバーライドし、.to_representation(self, value) メソッドを実装する必要があります。このメソッドは、フィールドのターゲットを value 引数として受け取り、ターゲットをシリアライズするために使用する必要がある表現を返す必要があります。value 引数は、通常、モデルインスタンスになります。

読み取り/書き込み可能なリレーショナルフィールドを実装する場合は、.to_internal_value(self, data) メソッドも実装する必要があります。

contextに基づいて動的なクエリセットを提供するには、クラス上で.querysetを指定したり、フィールドを初期化する際に指定する代わりに、.get_queryset(self)をオーバーライドすることもできます。

例えば、トラックをその順序、タイトル、および時間を使用してカスタム文字列表現にシリアライズするリレーションフィールドを定義できます。

import time

class TrackListingField(serializers.RelatedField):
    def to_representation(self, value):
        duration = time.strftime('%M:%S', time.gmtime(value.duration))
        return 'Track %d: %s (%s)' % (value.order, value.name, duration)

class AlbumSerializer(serializers.ModelSerializer):
    tracks = TrackListingField(many=True)

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

このカスタムフィールドは、次のような表現にシリアライズされます。

{
    'album_name': 'Sometimes I Wish We Were an Eagle',
    'artist': 'Bill Callahan',
    'tracks': [
        'Track 1: Jim Cain (04:39)',
        'Track 2: Eid Ma Clack Shaw (04:19)',
        'Track 3: The Wind and the Dove (04:34)',
        ...
    ]
}

カスタムハイパーリンクフィールド

場合によっては、単一のルックアップフィールド以上のものを必要とするURLを表現するために、ハイパーリンクフィールドの動作をカスタマイズする必要があるかもしれません。

これは、HyperlinkedRelatedFieldをオーバーライドすることで実現できます。オーバーライドできるメソッドは2つあります。

get_url(self, obj, view_name, request, format)

get_urlメソッドは、オブジェクトインスタンスをそのURL表現にマッピングするために使用されます。

view_nameおよびlookup_field属性がURL設定に正しく一致するように構成されていない場合、NoReverseMatchが発生する可能性があります。

get_object(self, view_name, view_args, view_kwargs)

書き込み可能なハイパーリンクフィールドをサポートしたい場合は、受信URLをそれが表すオブジェクトにマッピングするために、get_objectもオーバーライドする必要があります。読み取り専用のハイパーリンクフィールドの場合、このメソッドをオーバーライドする必要はありません。

このメソッドの戻り値は、一致するURL設定引数に対応するオブジェクトである必要があります。

ObjectDoesNotExist例外が発生する可能性があります。

例えば、次のように、2つのキーワード引数を取る顧客オブジェクトのURLがあるとします。

/api/<organization_slug>/customers/<customer_pk>/

これは、単一のルックアップフィールドのみを受け入れるデフォルトの実装では表現できません。

この場合、必要な動作を実現するためにHyperlinkedRelatedFieldをオーバーライドする必要があります。

from rest_framework import serializers
from rest_framework.reverse import reverse

class CustomerHyperlink(serializers.HyperlinkedRelatedField):
    # We define these as class attributes, so we don't need to pass them as arguments.
    view_name = 'customer-detail'
    queryset = Customer.objects.all()

    def get_url(self, obj, view_name, request, format):
        url_kwargs = {
            'organization_slug': obj.organization.slug,
            'customer_pk': obj.pk
        }
        return reverse(view_name, kwargs=url_kwargs, request=request, format=format)

    def get_object(self, view_name, view_args, view_kwargs):
        lookup_kwargs = {
           'organization__slug': view_kwargs['organization_slug'],
           'pk': view_kwargs['customer_pk']
        }
        return self.get_queryset().get(**lookup_kwargs)

このスタイルをジェネリックビューとともに使用したい場合は、正しいルックアップ動作を得るためにビューの.get_objectもオーバーライドする必要があることに注意してください。

一般的に、API表現には可能な限りフラットなスタイルをお勧めしますが、ネストされたURLスタイルも適度に使う場合は合理的です。


補足

queryset引数

queryset引数は、書き込み可能なリレーションシップフィールドでのみ必要とされます。その場合、プリミティブなユーザー入力からモデルインスタンスへのマッピングを行うモデルインスタンスのルックアップを実行するために使用されます。

バージョン2.xでは、ModelSerializerクラスが使用されている場合、シリアライザクラスがqueryset引数を自動的に決定できることがありました

この動作は、書き込み可能なリレーションフィールドでは明示的なqueryset引数を常に使用する動作に置き換えられました。

そうすることで、ModelSerializerが提供する隠された「魔法」の量を減らし、フィールドの動作をより明確にし、ModelSerializerのショートカットを使用したり、完全に明示的なSerializerクラスを使用したりする間を簡単に移行できるようにします。

HTML表示のカスタマイズ

モデルの組み込みの__str__メソッドは、choicesプロパティを設定するために使用されるオブジェクトの文字列表現を生成するために使用されます。これらの選択肢は、ブラウズ可能なAPIの選択HTML入力を設定するために使用されます。

このような入力のカスタム表現を提供するには、RelatedFieldサブクラスのdisplay_value()をオーバーライドします。このメソッドはモデルオブジェクトを受け取り、それを表すのに適切な文字列を返す必要があります。例:

class TrackPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField):
    def display_value(self, instance):
        return 'Track: %s' % (instance.title)

セレクトフィールドのカットオフ

ブラウズ可能なAPIでレンダリングされると、リレーションフィールドはデフォルトで最大1000個の選択可能な項目のみを表示します。より多くの項目が存在する場合は、「1000以上の項目…」と表示された無効なオプションが表示されます。

この動作は、非常に多くのリレーションシップが表示されるため、テンプレートが許容可能な時間内にレンダリングできなくなるのを防ぐことを目的としています。

この動作を制御するために使用できる2つのキーワード引数があります。

  • html_cutoff - 設定した場合、これはHTMLセレクトドロップダウンによって表示される選択肢の最大数になります。制限を無効にするにはNoneに設定します。デフォルトは1000です。
  • html_cutoff_text - 設定した場合、HTMLセレクトドロップダウンで最大項目数が切り捨てられた場合にテキストインジケーターが表示されます。デフォルトは"More than {count} items…"です。

設定HTML_SELECT_CUTOFFおよびHTML_SELECT_CUTOFF_TEXTを使用して、これらをグローバルに制御することもできます。

カットオフが適用されている場合、代わりにHTMLフォームでプレーンな入力フィールドを使用したい場合があります。styleキーワード引数を使用してそれを行うことができます。例:

assigned_to = serializers.SlugRelatedField(
   queryset=User.objects.all(),
   slug_field='username',
   style={'base_template': 'input.html'}
)

逆リレーション

リバースリレーションシップは、ModelSerializerクラスおよびHyperlinkedModelSerializerクラスによって自動的に含まれるわけではないことに注意してください。リバースリレーションシップを含めるには、フィールドリストに明示的に追加する必要があります。例:

class AlbumSerializer(serializers.ModelSerializer):
    class Meta:
        fields = ['tracks', ...]

通常、フィールド名として使用できるリレーションシップに適切なrelated_name引数が設定されていることを確認する必要があります。例:

class Track(models.Model):
    album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE)
    ...

リバースリレーションシップに関連名をまだ設定していない場合は、fields引数で自動的に生成された関連名を使用する必要があります。例:

class AlbumSerializer(serializers.ModelSerializer):
    class Meta:
        fields = ['track_set', ...]

詳細については、リバースリレーションシップに関するDjangoドキュメントを参照してください。

ジェネリックリレーション

汎用外部キーをシリアライズする場合は、関係のターゲットをどのようにシリアライズするかを明示的に決定するために、カスタムフィールドを定義する必要があります。

例えば、他の任意のモデルとの汎用的な関係を持つタグの次のモデルがあるとします。

class TaggedItem(models.Model):
    """
    Tags arbitrary model instances using a generic relation.

    See: https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/
    """
    tag_name = models.SlugField()
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    tagged_object = GenericForeignKey('content_type', 'object_id')

    def __str__(self):
        return self.tag_name

そして、タグが関連付けられている可能性のある次の2つのモデルがあります。

class Bookmark(models.Model):
    """
    A bookmark consists of a URL, and 0 or more descriptive tags.
    """
    url = models.URLField()
    tags = GenericRelation(TaggedItem)


class Note(models.Model):
    """
    A note consists of some text, and 0 or more descriptive tags.
    """
    text = models.CharField(max_length=1000)
    tags = GenericRelation(TaggedItem)

各インスタンスのタイプを使用して、シリアライズ方法を決定し、タグ付けされたインスタンスをシリアライズするために使用できるカスタムフィールドを定義できます。

class TaggedObjectRelatedField(serializers.RelatedField):
    """
    A custom field to use for the `tagged_object` generic relationship.
    """

    def to_representation(self, value):
        """
        Serialize tagged objects to a simple textual representation.
        """
        if isinstance(value, Bookmark):
            return 'Bookmark: ' + value.url
        elif isinstance(value, Note):
            return 'Note: ' + value.text
        raise Exception('Unexpected type of tagged object')

関係のターゲットをネストされた表現にする必要がある場合は、.to_representation()メソッド内で必要なシリアライザを使用できます。

    def to_representation(self, value):
        """
        Serialize bookmark instances using a bookmark serializer,
        and note instances using a note serializer.
        """
        if isinstance(value, Bookmark):
            serializer = BookmarkSerializer(value)
        elif isinstance(value, Note):
            serializer = NoteSerializer(value)
        else:
            raise Exception('Unexpected type of tagged object')

        return serializer.data

GenericRelationフィールドを使用して表現されたリバース汎用キーは、リレーションシップのターゲットのタイプが常に既知であるため、通常のリレーションフィールドタイプを使用してシリアライズできることに注意してください。

詳細については、汎用リレーションに関するDjangoドキュメントを参照してください。

Throughモデルを持つManyToManyFields

デフォルトでは、throughモデルが指定されたManyToManyFieldをターゲットとするリレーションフィールドは読み取り専用に設定されます。

throughモデルを持つManyToManyFieldを指すリレーションフィールドを明示的に指定する場合は、必ずread_onlyTrueに設定してください。

throughモデルの追加フィールドを表現する場合は、throughモデルをネストされたオブジェクトとしてシリアライズできます。


サードパーティパッケージ

次のサードパーティパッケージも利用可能です。

DRFネストされたルーター

drf-nested-routersパッケージは、ネストされたリソースを操作するためのルーターとリレーションシップフィールドを提供します。

Rest Frameworkジェネリックリレーション

rest-framework-generic-relationsライブラリは、汎用外部キーの読み取り/書き込みシリアライズを提供します。