DEV Community

Cover image for How to add tags to your models in Django | Django Packages Series #1
Rashid
Rashid

Posted on • Updated on

How to add tags to your models in Django | Django Packages Series #1

This post cross-published with OnePublish

Hey DEVs!

Welcome to the first post of Django Package Series. In this tutorial, you will learn how to add tagging functionality to your models.

Categories and tags help you organize your web site or blog and help your users find the information they want. A blog category is a topic you address on your blog. Your category list is like the table of contents for your blog. A tag is more specific and addresses items you discuss in a particular blog post. A tag is usually only a word or two and reflects the keywords or points of your article. If categories are your blog’s table of contents, tags are your blog’s index. By tagging an article with relevant key words, user can find the information easily so it makes your blog more professional.

django-taggit is a reusable application that primarily offers you a Tag model, and a manager for easily adding tags to any model. We will create very simple blog app and implement tagging system in it.

I am assuming that you already created Django project. Let's Start with installing package by following command:

pip3 install django-taggit
Enter fullscreen mode Exit fullscreen mode

Once its installed, open settings.py and include taggit in your INSTALLED_APPS

INSTALLED_APPS = [
    ...
    'taggit'
]

Enter fullscreen mode Exit fullscreen mode

Create Model

Now, open models.py and create Post model:

from django.db import models
from taggit.managers import TaggableManager

class Post(models.Model):
    title = models.CharField(max_length=250)
    description = models.TextField()
    published = models.DateField(auto_now_add=True)
    slug = models.SlugField(unique=True, max_length=100)
    tags = TaggableManager()

    def __str__(self):
        return self.title
Enter fullscreen mode Exit fullscreen mode

The TaggableManager will show up automatically as a field in a ModelForm or in the admin. Tags input via the form field are parsed as follows:

  • If the input doesn’t contain any commas or double quotes, it is simply treated as a space-delimited list of tag names.
  • If the input does contain either of these character
    • Groups of characters which appear between double quotes take precedence as multi-word tags (so doublequoted tag names may contain commas). An unclosed double quote will be ignored.
    • Otherwise, if there are any unquoted commas in the input, it will be treated as comma-delimited. If not, itwill be treated as space-delimited.

django-taggit

Create Form

In your forms.py:

from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = [
            'title',
            'description',
            'tags',
        ]
Enter fullscreen mode Exit fullscreen mode

We included tags to our ModelForm but we are not going to render it by Django’s template language.

Create Views

Let's see views.py:

from django.shortcuts import render, get_object_or_404
from django.template.defaultfilters import slugify

from .models import Post
from .forms import PostForm
from taggit.models import Tag


def home_view(request):
    posts = Post.objects.order_by('-published')
    # Show most common tags 
    common_tags = Post.tags.most_common()[:4]
    form = PostForm(request.POST)
    if form.is_valid():
        newpost = form.save(commit=False)
        newpost.slug = slugify(newpost.title)
        newpost.save()
        # Without this next line the tags won't be saved.
        form.save_m2m()
    context = {
        'posts':posts,
        'common_tags':common_tags,
        'form':form,
    }
    return render(request, 'home.html', context)

def detail_view(request, slug):
    post = get_object_or_404(Post, slug=slug)
    context = {
        'post':post,
    }
    return render(request, 'detail.html', context)

def tagged(request, slug):
    tag = get_object_or_404(Tag, slug=slug)
    # Filter posts by tag name  
    posts = Post.objects.filter(tags=tag)
    context = {
        'tag':tag,
        'posts':posts,
    }
    return render(request, 'home.html', context)
Enter fullscreen mode Exit fullscreen mode

When saving a form, you have to use the commit=False option and call save_m2m() on the form after you save the object.

We are using slugify to convert our post title (string) to valid slug.

As you see you can filter posts by tag name and display most used tags.

Create Template

Great! Now we can create our templates.

base.html

<html>
    <head>
        <title>Simple Blog</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
        <link rel="stylesheet" href="/static/css/tagsinput.css" />
    </head>

    <body>
    {% block content %}{% endblock content %}
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
    <script src="/static/js/tagsinut.js"></script>
    <script>
    $("#post-form").submit(function(e){
        e.preventDefault();
        });</script>
    </body>
</html>
Enter fullscreen mode Exit fullscreen mode

home.html

{% extends 'base.html' %}

{% block content %}
        <div class="container pt-5">
            <form method="POST">
                {% csrf_token %}
                <div class="form-group">
                    <label>Title</label>
                    <input type="text" class="form-control" name="title" placeholder="Add title">
                </div>
                <div class="form-group">
                    <label>Description</label>
                    <textarea type="text" class="form-control" name="description" placeholder="Add description"></textarea>
                </div>
                <div class="form-group">
                    <label>Tags</label>
                    <input type="text" data-role="tagsinput" class="form-control" name="tags">
                </div>
                <button type="submit" class="btn btn-primary">Submit</button>
            </form>
            <p>Common Tags: 
            {% for mt in common_tags %}
                <a href="#" class="badge badge-success">{{mt}}</a>
            {% endfor %}
            </p>
            <div class="row mb-2 posts">
                    {% for post in posts %}
                    <div class="col-md-6">
                        <div class="cards">
                            <div class="row no-gutters border rounded  flex-md-row mb-4 shadow-sm h-md-250">
                                <div class="col p-4 d-flex flex-column position-static">
                                    <h3 class="my-1"><a href="{% url 'detail' post.slug %}">{{post.title}}</a></h3>
                                    <div style="display:flex">
                                        {% for tag in post.tags.all %}
                                        <a href="{% url 'tagged' tag.slug %}" class="mr-1 badge badge-info">#{{ tag }}</a>
                                        {% endfor %}
                                    </div>
                                    <p class="mb-auto">{{post.description}}</p>
                                    <p class="mb-auto text-muted">{{post.published}}</p>
                                </div>
                            </div>
                        </div>       
                    </div>
                    {% endfor %}
            </div>
        </div>    
{% endblock content %}               
Enter fullscreen mode Exit fullscreen mode

In form tag you can see name attribute and its just our form field name as attribute in HTML Template. We have to override the tag input so that is the main reason why we didn't render it by Django’s template language.

We are using jQuery plugin which provides a Twitter Bootstrap user interface for managing tags. This plugin basically will change text inputs to actual tags. Take a look Bootstrap Tags Input

Here is the final results:

and filter posts by tag:

Check documentation of django-taggit for more.

You can clone or download the project from my GitHub

GitHub logo thepylot / django-taggit-tutorial

Add tags to your models with django-taggit | Django Packages Series

django-taggit-tutorial

Add tags to your models with django-taggit | Django Packages Series #1

Getting Started

This project works on Python 3+ and Django 2+.

Install dependencies:

python3 -m pip3 install -r requirements.txt

then run following commands:

python3 manage.py makemigrations posts
python3 manage.py migrate
python3 manage.py runserver

That's it! Please share it and follow me (your support is very important) on social media! :) Also check Reverse Python and as always Stay Connected!🚀

Buy me a coffee :)
Instagram
Twitter

Top comments (6)

Collapse
 
davidmm1707 profile image
David MM👨🏻‍💻 • Edited

Cool. I missed this feature and I didn't know the best way to implement it on my own, so I'm going to give this a try.

Collapse
 
thedevtimeline profile image
Rashid

Great! Let me know your experience👍🚀

Collapse
 
billy_de_cartel profile image
Billy Okeyo

I tried everything there but I'm getting a problem with saving my tags from the admin side. If I edit an already created post, it saves well but if a create a new post then it doesn't save. Any help

Collapse
 
omidshojaee profile image
Omid Shojaee

Hello,
Would you please tell me what you use to highlight the code in this post? Thank you.

Collapse
 
jcroot profile image
Jean-Claude

Hi, I saw your solution, but I have a question, what happen when your type a tags with whitespace? Like, alcohol con gel (gel glicol).

Collapse
 
paulnorthern profile image
PaulNorthern

Hey dude, how do I split tags by pressing the space? I have to click on the screen or print a comma, to separate the tags