user2896120
user2896120

Reputation: 3282

Adding field to serializer with 2 foreign keys

I have 2 models that look like this:

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)

If the topic is already in the Topic table, then we return the id of that topic. We do not create another. When a user submits a Post with tagged topics (think of it as hashtags), then if those topics don't exist in the Topic table they will be created along with the relationship in PostTopic

That being said, here's how my serializer looks:

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

Currently, my serializer gives me an AttributeError, because topics is not a field in Post. How can I fix this so that I can make it work? If I use PostTopic then how would I get the user to give the actual topics?

Upvotes: 0

Views: 131

Answers (1)

ruddra
ruddra

Reputation: 51988

I think you are looking for a Many To Many relationship between Post and Topic. You can add it like this:

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)

Also, your method of handling duplicates won't work because model's save method does not return anything. Let us handle it in serializer with with some minor fixes:

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

Upvotes: 1

Related Questions