Django – Extract hash tags from a post and save them all in a many-to-one relation

Issue

So I have this code below that when a user submits a post it extracts all the hash tags from it then creates a new entry in the hash tag table and also creates a new reference in the HashTagsInPost table so people can search posts by hash tags. My primary concerns lay in the Views.py file.

Problems:

  1. I don’t think it’s good to iterate over the list of hash tags pulled out and doing a .get() call to see if exists then if it doesn’t create it. There should be a better way to do this.
  2. Since HashTagsInPost is a one-to-many relation, I don’t really know how to store that lists of Hash Tags as the hash_tag attribute in the HashTagsInPost attribute. Do I just pass a list of HashTag objects?

Views.py

def post(self, request):
    serializer = PostSerializer(data=request.data)

    if serializer.is_valid():
        post_body = request.data['body']
        post_obj = serializer.save()
        hash_tags_list = extract_hashtags(post_body)

        for ht in hash_tags_list:
            try:
                ht_obj = HashTags.objects.get(pk=ht)
            except HashTags.DoesNotExist:
                ht_obj = HashTags.objects.create(hash_tag=ht)
            
            HashTagsInPost.objects.create(hash_tag=ht_obj, post_id=)

        return Response(serializer.data, status=status.HTTP_201_CREATED)
    else:
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Models.py

class HashTags(models.Model):
    hash_tag = models.CharField(max_length=140, primary_key=True)  

class Post(AbstractBaseModel):
    creator_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name="post_creator_id", db_index=True)
    goal_id = models.ForeignKey(Goal, on_delete=models.CASCADE, db_index=True)
    body = models.CharField(max_length=511)

class HashTagsInPost(AbstractBaseModel):
    hash_tag = models.ForeignKey(HashTags, on_delete=models.CASCADE, db_index=True)
    post_id = models.OneToOneField(Post, on_delete=models.CASCADE)

Solution

Firstly from your use case a Post can be related to multiple HashTags (note: model names should ideally be singular) objects. Whereas with your current modelling a post can have only one hashtag. To model a many to many relationship you should use a ManyToManyField [Django docs] which will internally make a junction table having foreign keys to the two related tables:

class HashTags(models.Model):
    hash_tag = models.CharField(max_length=140, primary_key=True)  


class Post(AbstractBaseModel):
    creator_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name="post_creator_id", db_index=True)
    goal_id = models.ForeignKey(Goal, on_delete=models.CASCADE, db_index=True)
    body = models.CharField(max_length=511)
    # Add a m2m with HashTags
    hash_tags = models.ManyToManyField(HashTags, related_name='posts')

Now for your problem of getting / creating the tag, you can use the get_or_create function:

def post(self, request):
    serializer = PostSerializer(data=request.data)

    if serializer.is_valid():
        post_body = request.data['body']
        post_obj = serializer.save()
        hash_tags_list = extract_hashtags(post_body)
        hash_tags = [HashTags.objects.get_or_create(hash_tag=ht)[0] for ht in hash_tags_list]
        post_obj.hash_tags.set(hash_tags)

        return Response(serializer.data, status=status.HTTP_201_CREATED)
    else:
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Answered By – Abdul Aziz Barkat

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published