Django 序列化程序根据外键关系列表的一侧显示不同的字段

Django serializer to show different fields based on the side of a foreign key relation list

我正在为电影和演员编写一个 Django API(使用 Django REST 框架),并将它们放在单独的 table 中,另一个 table 用于存储每部电影的演员列表存储两者的密钥。我对它们都有不同的模型和序列化器。我想知道是否有可能根据我得到的是电影还是演员,为两者之间的连接显示不同的字段选择(table 用于按电影存储演员列表)。换句话说,当我拿到电影时,我只想要演员和角色字段,但当我拿到演员时,我只想要电影和角色字段。获取与当前所选对象相关的字段将是多余的,而且会变得令人讨厌和混乱(当我获得安东尼霍普金斯的电影角色列表时,为什么我需要知道安东尼霍普金斯在每部电影中?)。

以下是模型的简化版本:

class Film(models.Model):
    title = models.CharField(max_length=100, null=False, blank=False)
    ...Plus other fields

    def __str__(self):
        return f"{self.title}"

    class Meta:
        ordering = ['title']

class Person(models.Model):
    name = models.CharField(max_length=100, null=False, blank=False)
    ...Plus other fields

    def __str__(self):
        return f"{self.name}"

    class Meta:
        ordering = ['name']

class ActorRole(models.Model):
    film = models.ForeignKey(
        Film, on_delete=models.CASCADE, related_name='cast', null=False, blank=False)
    person = models.ForeignKey(
        Person, on_delete=models.CASCADE, related_name='filmCredits', null=False, blank=False)
    role = models.CharField(max_length=100, null=False, blank=False)

    def __str__(self):
        return f"{self.film}: {self.person.name} - {self.role}"

    class Meta:
        ordering = ['film__title']
        unique_together = ['film', 'person', 'role']

这里是序列化程序的简化版本。

class FilmSerializer(serializers.ModelSerializer):
    cast = ActorRoleSerializer(many=True, read_only=True)

    class Meta:
        model = Film
        fields = ['id', 'title', 'cast']

class PersonSerializer(serializers.ModelSerializer):
    filmCredits = ActorRoleSerializer(many=True, read_only=True)

    class Meta:
        model = Person
        fields = ['id', 'name', 'filmCredits']

class ActorRoleSerializer(serializers.ModelSerializer):
    film = serializers.CharField(source='film.title')
    actor = serializers.CharField(source='person.name')

    class Meta:
        model = ActorRole
        fields = ['actor', 'role', 'film']

以下是我取回胶片时希望得到的:

{
  "title": "Doctor Strange (2016)"
  ...other fields

  "cast": [
    {
      "actor": "Pumpernickle Samsquanch",
      "role": "Dr. Stephen Strange"
    }
    {
      "actor": "Benedict Wong",
      "role": "Wong"
    }
    {
      "actor": "Mads Mikkelsen",
      "role": "Kaecilius"
    }
    ...etc
  ]
}

以下是我检索演员时希望得到的内容:

{
  "name": "Mads Mikkelsen",
  ...other fields

  "filmCredits": [
    {
      "film": "Casino Royale (2006)",
      "role": "Le Chiffre"
    },
    {
      "film": "Hunt, The (2012)",
      "role": "Lucas"
    }
    {
      "film": "Doctor Strange (2016)",
      "role": "Kaecilius"
    }
    ...etc
  ]
}

注意 film 如何在 cast 列表中获取演员和角色字段而不是 film 字段以及演员如何在 filmCredits 列表中获取电影和角色字段而不是 actor 字段。

我想这在某种程度上是可能的,但我不知道如何做。我考虑制作两个单独的序列化程序,一个用于关系的每一方,但这似乎有点愚蠢。我想我可能需要对我的模型进行一些修改,所以我对此持开放态度。我也完全希望有人指出我对整个事情的看法都是错误的。无论哪种方式,我都希望在不添加大量额外样板代码的情况下获得所需的输出。

您可以使用 DRF docs:

中的这个漂亮的 class 支持动态修改序列化程序的字段
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
    """
    A ModelSerializer that takes an additional `fields` argument that
    controls which fields should be displayed.
    """

    def __init__(self, *args, **kwargs):
        # Don't pass the 'fields' arg up to the superclass
        fields = kwargs.pop('fields', None)

        # Instantiate the superclass normally
        super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)

        if fields is not None:
            # Drop any fields that are not specified in the `fields` argument.
            allowed = set(fields)
            existing = set(self.fields)
            for field_name in existing - allowed:
                self.fields.pop(field_name)

有了它你可以做这样的事情:

class FilmSerializer(serializers.ModelSerializer):
    cast = ActorRoleSerializer(many=True, read_only=True, fields=['actor', 'role'])

    class Meta:
        model = Film
        fields = ['id', 'title', 'cast']


class PersonSerializer(serializers.ModelSerializer):
    filmCredits = ActorRoleSerializer(many=True, read_only=True, fields=['film', 'role'])

    class Meta:
        model = Person
        fields = ['id', 'name', 'filmCredits']


class ActorRoleSerializer(DynamicFieldsModelSerializer):
    film = serializers.CharField(source='film.title')
    actor = serializers.CharField(source='person.name')

    class Meta:
        model = ActorRole
        fields = ['actor', 'role', 'film']

您现在可以设置您希望 ActorRoleSerializer 使用的字段,具体取决于使用它的序列化程序。