首页 > 解决方案 > 使用 2 个外键将字段添加到序列化程序

问题描述

我有 2 个看起来像这样的模型:

class Topic(models.Model):
    name = models.CharField(max_length=25, unique=True)

    def save(self, *args, **kwargs):
        topic = Topic.objects.filter(name=self.name)

        if topic:
            return topic[0].id

        super(Topic, self).save(*args, **kwargs)
        return self.id

class PostTopic(models.Model):
    topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
    post= models.ForeignKey(Post, on_delete=models.CASCADE)

如果主题已经在Topic表中,那么我们返回该主题的 id。我们不创造另一个。当用户提交Post带有标签的主题(将其视为主题标签)时,如果这些主题不存在于Topic表中,则它们将与中的关系一起创建PostTopic

话虽如此,这就是我的序列化程序的外观:

class PostSerializer(serializers.ModelSerializer):
    topics = serializers.ListField(
         child=serializers.CharField(max_length=256), max_length=3
    )

    class Meta:
        model = Post
        fields = ('user', 'status', 'topics')

    def create(self, validated_data):
        topics= validated_data.pop('topics')
        post = Post.objects.create(**validated_data)

        for topic in topics:
             topic_id = Topic(name=topic).save()
             PostTopic(post_id=post.id, topic_id=topic_id).save()

        return post

目前,我的序列化程序给了我一个AttributeError, 因为主题不是Post. 我该如何解决这个问题才能让它工作?如果我使用PostTopic,那么我将如何让用户给出实际的主题?

标签: djangodjango-modelsdjango-rest-framework

解决方案


我认为您正在寻找和之间的Many To Many关系。您可以像这样添加它:PostTopic

class Post(models.Model):
    # rest of the fields
    topics = models.ManyToManyField('Topic', through='PostTopic')

class Topic(models.Model):
    name = models.CharField(max_length=25, unique=True)

   # def save(self, *args, **kwargs):
   #     **Important:** Django model's save function does not return anything. So handling it here won't be good. Let us handle duplicate entry in serializer
   #     topic = Topic.objects.filter(name=self.name)

   #     if topic:
   #         return topic[0].id

   #     super(Topic, self).save(*args, **kwargs)
   #     return self.id

class PostTopic(models.Model):
    topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
    post= models.ForeignKey(Post, on_delete=models.CASCADE)

此外,您处理重复项的方法将不起作用,因为模型的保存方法不返回任何内容。让我们在序列化程序中处理它并进行一些小修复:

class PostSerializer(serializers.ModelSerializer):
    topics = serializers.ListField(
         child=serializers.CharField(max_length=256), max_length=3
    )

    class Meta:
        model = Post
        fields = ('user', 'status', 'topics')

    def create(self, validated_data):
        topics= validated_data.pop('topics')
        post = Post.objects.create(**validated_data)

        for topic in topics:
             new_topic, _ = Topic.objects.get_or_create(name=topic)
             PostTopic(post_id=post.id, topic=new_topic).save()

        return post

推荐阅读