DRFでGeneric ForeignKey を扱う
GenericForeignKeyの扱い方はDRFドキュメントにある
Serializer relations - Django REST framework
このドキュメントの解釈を行う。自分が作った例は時間があれば、Githubにあげておく。
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
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')
1つ目のpart
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
このモデルは単にDjangoのプロジェクトにGeneric ForeignKeyを設けたモデルを定義しているだけだ。Generic ForeignKeyをどう設けるのかは別の資料をチェックしてもらいたい。このGeneric ForeignKeyが存在することでDRFの出力が困るという人に以下を続けて読んでもらいたい。
2つ目のpart
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)
2つ目のパートでもDRFでとくにGeneric ForeignKeyを表示するために何か特別なことをしているわけではない。
ちなみにGenericRelationはGenericForeignKeyで定めたモデルデータの属性を表示する役割である。
参考:The contenttypes framework | Django documentation | Django
3つ目のpart
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')
ここでの要点は2点ある。
DRFでForeignKeyのモデルを表示するためにserializers.RelatedFieldを継承したクラスを作成している。
通常のForeignKeyを参照する方法をアレンジしto_representationメソッドで返すデータをクラスによって場合分けを行っている。
あとはこの継承したサブクラスをGeneric ForeignKeyを設定したモデルのSerializerの属性値として設定してやれば、DRFの出力としてGeneric ForeignKeyも出力できるようになる。 属性値としては、read_only=Trueを引数にすることも忘れずに。無いとエラーが出てしまうから。
関連記事:DRFでForeign Keyの値を参照するやり方 - diadia
参考
python - How to Serialize generic foreign key In DRF - Stack Overflow