Learn Wagtail Course

Adding Tags to Pages

Tags and categories are very similar, but ultimately they serve a different purpose. We've already covered how to add categories, and in this tutorial we're learning how to add tags to Wagtail CMS pages. The nice thing about Tags is they come with an autocomplete feature. Because tags can be easily forgotten, Wagtail helps us re-use tags by giving an autocomplete feature (suggestions based on what you're currently typing).

The Through Model

Tags are an interesting subject in Wagtail. We use a package called Taggit which does a lot of work behind the scenes and makes implementing tags very easy. Before we can add tags to a page, we need to add a new Django Model.

Note: We're adding tags to a Blog Detail Page, but you can adjust this to work on any page.

class BlogPageTag(TaggedItemBase):
    content_object = ParentalKey(
        'BlogDetailPage',
        related_name='tagged_items',
        on_delete=models.CASCADE,
    )

Adding the Tags Field

Next we need to add the tags field to our BlogDetailPage.

class BlogDetailPage(Page):
    # Fields here...

    tags = ClusterTaggableManager(through=BlogPageTag, blank=True)

    content_panels = Page.content_panels + [
        # Custom panels 
        FieldPanel("tags"),
    ]

Lastly, the Template

Lastly we need to add tag links to our Blog Detail Page. You can copy and paste this code into your blog_detail_page.html (or whichever template you're applying tags to)

{# Check if there are tags #}
    {% if page.tags.count %}
        <h1>Tags:</h1>
        {% for tag in page.tags.all %}
            {# Loop through all the existing tags #}
            <a href="{{ self.get_parent.url }}?tag={{ tag.slug }}">{{ tag }}</a><br />
        {% endfor %}
    {% endif %}

Filtering Blog Posts by Tag

This part is bonus, but if you wanted to click one of the tags in the code snippet above you'll want your Blog Listing Page (A.K.A. Blog Index Page) to filter your posts. In the course we've added pagination, so make sure you read the entire file for pagination. Below is just a simple filter.

class BlogListingPage(RoutablePageMixin, Page):
    """Listing page lists all the Blog Detail Pages."""

    # Fields and panels

    def get_context(self, request, *args, **kwargs):
        """Adding custom stuff to our context."""
        context = super().get_context(request, *args, **kwargs)
        # Get all posts
        all_posts = BlogDetailPage.objects.live().public().order_by('-first_published_at')

        if request.GET.get('tag', None):
            tags = request.GET.get('tag')
            all_posts = all_posts.filter(tags__slug__in=[tags])

        context["posts"] = all_posts
        return context

The above code will check for ?tag=your-tag-slug-here in your URL and filter your tags based on the slug of the tag. So when you click a tag link on a detail page, it will bring you to a Blog Listing Page that only shows blog posts with that particular tag.

The Git Commit

As always, the code is completely open source. Here's the direct link to the GitHub commit: https://github.com/CodingForEverybody/learn-wagtail/commit/8d4240096482baa207ca7bef31bf5f8aed263f19

Was this helpful to you?

Sharing is caring. Help the community by sharing this article.