Building a Social Media App With Python 3 & Django Beginners Tutorial 18: Tags (Part 1 of 2)

Video Tutorial

Code on Github

In this tutorial we will add the basic functionality for tagging in posts and comments. To do this, we will add a new field to our post and comment model, we will add a method to those models as well and then to finish it up we will add some Javascript to change all of the tag text to be links. We will add the rest of the functionality for an explore page and the links in the next tutorial.

Adding Tags to the Models

First we just need to add a new field to the models. Here is the completed models. We will then need to create a new Tag model for this field.

class Post(models.Model):  shared_body = models.TextField(blank=True, null=True)  body = models.TextField()  image = models.ManyToManyField('Image', blank=True)  created_on = models.DateTimeField(default=timezone.now)  shared_on = models.DateTimeField(blank=True, null=True)  author = models.ForeignKey(User, on_delete=models.CASCADE)  shared_user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name='+')  likes = models.ManyToManyField(User, blank=True, related_name='likes')  dislikes = models.ManyToManyField(User, blank=True, related_name='dislikes')  tags = models.ManyToManyField('Tag', blank=True)class Comment(models.Model):  comment = models.TextField()  created_on = models.DateTimeField(default=timezone.now)  author = models.ForeignKey(User, on_delete=models.CASCADE)  post = models.ForeignKey('Post', on_delete=models.CASCADE)  likes = models.ManyToManyField(User, blank=True, related_name='comment_likes')  dislikes = models.ManyToManyField(User, blank=True, related_name='comment_dislikes')  parent = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True, related_name='+')  tags = models.ManyToManyField('Tag', blank=True)class Tag(models.Model):  name = models.CharField(max_length=255)

Next we need to add a method that will find any tags in the text and add create a tag for them. We will create an instance of the Tag model to hold the data and then add it to the field. For the post we will do this for both the body and the shared_body. For the comment we will do this for the comment field.

Here is the Post:

def create_tags(self):  for word in self.body.split():    if (word[0] == '#'):      tag = Tag.objects.get(name=word[1:])      if tag:        self.tags.add(tag.pk)      else:        tag = Tag(name=word[1:])        tag.save()        self.tags.add(tag.pk)      self.save()  for word in self.shared_body.split():    if (word[0] == '#'):      tag = Tag.objects.get(name=word[1:])      if tag:        self.tags.add(tag.pk)      else:        tag = Tag(name=word[1:])        tag.save()        self.tags.add(tag.pk)      self.save()

And similarly for the Comment:

def create_tags(self):  for word in self.comment.split():    if (word[0] == '#'):      tag = Tag.objects.get(name=word[1:])      if tag:        self.tags.add(tag.pk)      else:        tag = Tag(name=word[1:])        tag.save()        self.tags.add(tag.pk)      self.save()

Now that we have our models finished, we need to add these methods to our views. We want to call them whenever a post or comment is created to add these tags.

Calling the Methods in Our Views

We want to call this in the post list and the post detail for when we create a post or when we create a comment.

In our Post List post method:

new_post.create_tags()

In our Post Detail post method:

new_comment.create_tags()

Now that we have our tags being saved on our models. The last step is to write some Javascript to be able to dynamically find these tags and link them to the explore page. Right now these links wont work because we don’t have the explore page but let’s set it up for the next tutorial where we will build that.

In our static/js/social.js we will add another function to handle this.

function formatTags() {  var elements = document.getElementsByClassName('body');  for (let i = 0; i < elements.length; i++) {    let bodyText = elements[i].children[0].innerText;    let words = bodyText.split(' ');    for (let j = 0; j < words.length; j++) {      if (words[j][0] === '#') {        replacedText = bodyText.replace(/\s\#(.*?)(\s|$)/g, ` <a href="#">${words[j]}</a>`);        elements[i].innerHTML = replacedText      }    }  }}formatTags();

NOTE — Anywhere you want tags, you will need to wrap the text in a div with a class of ‘body’

What we are doing here is we are finding all elements with the class of body and then we can loop through all of those elements and get the text, using .split(‘ ‘) we can split the text by each word and loop through those words to find any word that begins with a #. This is a tag. From here, we use regular expressions to match the tag text, we can use the replace method to replace this with an anchor tag. Finally we set the innerHTML of that element to be the new text. This will make the tag a link in the DOM. We are calling this method at the bottom of this file because we want it to run right away everytime the page is loaded.

This is where we will stop with today’s tutorial. You should be able to make a new post and the tags will be processed automatically. In the next tutorial we will build an explore page to find different tags and link each tag to the explore page filtered for that tag.