Wagtail CMS: Adding Tags to Pages

This video is from Wagtail CMS 2023 .

Wagtail CMS: Adding Tags to Pages
0:18:06
Published November 29, 2023
5,515 views

In this video we'll discover how to add Tags (with an autocomplete feature) to Wagtail Pages. We'll also learn how to display tags on a Blog Detail Page, and then we'll learn how to filter blog posts by a certain tag on the Blog Listing Page.

Tutorial: https://learnwagtail.com/course/adding-tags-to-wagtail-cms-pages/

Git Commit: https://github.com/CodingForEverybody/learn-wagtail/commit/8d4240096482baa207ca7bef31bf5f8aed263f19

Learn Wagtail from scratch with the official Wagtail for Beginners Course
https://learnwagtail.com/wagtail-for-beginners/

Used in this video: Wagtail 2.4, Python 3.7, Django 2.1.5

Outro Template made by Grabster - Youtube.com/GrabsterTV

#Wagtail #Django #Python

Summary

Wagtail tags are added by defining a through model that inherits from `TaggedItemBase` and links to the target page with a `ParentalKey`. The page then gets a `ClusterTaggableManager` using that through model, with `blank=True`, and a `FieldPanel` so editors can enter comma-separated tags in the admin. After creating and applying migrations, templates can loop over `page.tags.all`, display each tag and its slug, and link to a listing page with a `tag` query parameter. The listing view reads that parameter and filters live, public pages with `tags__slug__in`; the same field panel must be included explicitly on child page types whose content panels override the parent’s.

Key takeaways

  • Create a `TaggedItemBase` through model with a `ParentalKey` to the page type and a related name such as `tagged_items`.
  • Add `ClusterTaggableManager(through=..., blank=True)` to the page model and expose it with a `FieldPanel`.
  • Run `makemigrations` and `migrate` before using the new tags in the admin.
  • In templates, iterate over `page.tags.all` and use each tag’s name or slug when building links.
  • Filter the listing queryset with the requested tag slug, using `tags__slug__in` and a conditional so untagged requests show all posts.
  • Child page types that replace inherited content panels need their own `tags` field panel.

Summarised automatically from the transcript.

Transcript

3,203 words · auto-generated Show

Automatically transcribed, so expect mistakes in names and technical terms.

0:00

Hello and welcome back to another lesson on learning Wagtail. In this video, we are going to learn how to add tags to our blog pages. But you can add tags to any page. We're just going to use blog as an example. So the first thing I'm going to do here is I am going to open up my terminal and I'm going to go cd websites slash my site. That's where I'm storing my site. And I'm simply going to enter my virtual environment with pipenv. So I use pipenv shell, but you might be using Docker or VirtualEnv or Vagrant. virtual box whichever you're using make sure you get in there and start up your website and all we have to do is type manage dot py run server and that will automatically run our server on port

0:47

8000. So if we open up our website to localhost 8000 on any browser You'll see that our website is loaded. Okay, so I'm gonna go to slash admin and I want to edit a blog page and I want to show you what this looks like before and sort of after. So I'm going to edit a blog page and we already have categories, but we don't have tags. So that's what we're going to do is we're going to add tags now. So I'm going to open up my editor. I'm using Visual Studio Code. You might be using something else, but Visual Studio Code is free and seems to be popular, so that's what I'm using these days. And I'm going to open up blog slash models dot pie

1:33

and what I want to add is to my blog detail page that's inheriting from page Now the reason I'm specifying that is because uh can I maybe just collapse this whole thing? There we go. We have an article blog page which inherits from the blog detail page, and we don't want to add it to every single Child page, we just want to add it to the parent page. So what I'm going to do here is what do we have up here? We have a blog listing page, so let's go right above the blog detail page And in this section we are going to add a new class, and it's going to be called class blog page tags I guess it doesn't really matter because we're not going to be ex

2:18

accessing it uh through the name although if we were we would probably want to make that singular Uh it's going to inherit a tagged item base, which we have not imported yet, but we will in just a moment. And then we have a content object and all this is is a parental key and it's going to link to our blog page blog detail page rather because that's what it's called so that matches the class name of our blog detail page right here. We need to give it a related name and make that tooltip go away. We give it a related name. Related name is equal to, let's call it tagged items. And what happens when we delete?

3:03

Let's do models. cascade. Now that's all well and great, but we don't have a tagged item base. being imported. And we're actually going to need to import something else called a clusterable tag manager. So that was our blog tag class. And really all that is is a many to many table. And in our detail page, what we're going to do is add another item in here, another property rather, and we're going to add a clusterable or a cluster taggable manager. And so we just go into the page that we want to add our tags to, and I'm going to add tags right in here. Tags is equal to cluster taggable. manager. We're going to give it a through table.

3:51

Through is equal to blog page tag. And can this be blank by default? And we're going to say yes, this can be blank by default. Now when I save this page, my VS Code automatically says undefined name, cluster tagball manager. It should also be complaining about, yep, there it is, tagged item base. So let's scroll on up to the very top and let's import this. Are we importing model cluster? We already are, so let's put that in there sort of together. So from model cluster dot contrib. Tagit, import, cluster taggable manager. And we also need to import tagit, which is a package that automatically comes with Wagtail

4:39

So uh let's put that one just before Wagtail. From Tag it. models import tagged item base. And let's go back to our terminal real quick and we will see that we don't have any problems, so that's good. But if we refresh our page, yes, leave the page. We don't see our tagged items in here at all. And that's because in Wagtail, anything that you want to show up needs to be in a panel. So let's go and look for that page again. We've got a blog category in there. We have blog page tags, we're getting close. And you gotta be here, you are somewhere in here. So let's put you near the top. Let's create another field panel and this one's going to be called tags.

5:27

And tags, I just got that name from right here. So if I make that smaller Maybe we can see it on now we can't see it on one page or one view rather, but if I delete a bunch of stuff we can so this name fills in right there. That's all we're doing. Alrighty, so I go back to my browser and give that a refresh. And we still don't have any tags in here, so what's the deal with that? Well we are on an article blog page. What we said right here when we added our field panel with tags was add this to every parent page. But are we overwriting the content panels in a child page, in the article blog page? We might be. And in fact, we are.

6:13

So we want to overwrite this with tags. Alternatively, we could also use instead of page. content panels, we could be using blog detail page dot content panels and that will inherit from the class that we were just working on but we're not going to do that in this video so let's just save that article blog page and let's go and refresh Here we go. This looks better. So we see an error here, an operational error. And if you read this, what this is saying here is the exception value. It says no such table blog blog page tag. So basically we need to run some migrations to make a table in the database. So let's open up our terminal, let's cancel, and let's do

7:01

manage dot pie make migrations cool that made our migration for us and then lastly let's just migrate so that'll actually apply our changes And then we run our server. Run server. And that will have a new table for us. Okay, refresh the page. And here we can see we have tags. So this is pretty cool. So this tag could be tag one. I type comma and it'll just automatically make a tag. But if I have if I wanted a tag with multiple words, so Multiple worded tag. That's cool. All I do is hit comma and it makes a multi-worded tag for us. Now I'm going to save this. And it's at this point where we want to access our tags on our template.

7:48

And it's actually really, really easy to do on our template, just because the way Django works with its ORM. And the way that Wagtail makes everything nice and simple, we can simply access all of the tags and loop through them as if it was an inline panel or a Wagtail orderable. So what we're going to do is we just save that and I just want to click edit to make sure that that's saved properly. I know it did, but I just want to show you So we have tags in here. It reordered them for me. That's totally okay. And I'm just going to open up this template. So I want to open up the article blog page template and in here is where we're going to add our tags. So do do do do do where do we want to add tags? Let's just make this really ugly right up near the top and let's do H1

8:37

test. And let's go and view this page live. And we can see test up here. So that's where we want our tags to show up. So let's do this. We're going to call these tags. And we are going to create a for loop. So for page. tegs. all That's not right at all. For tag in there we go. So we got page. tegs. all. Page is the same as self So I'm just using the word page instead. Page. tegs. all. Remember if you get an error, chances are it's probably because you forgot the all. Otherwise you're just saying Get the property we're trying to loop through the property or if it's a method, loop through the method. It doesn't work, but if we do dot all that is the same as saying

9:23

execute the dot all method on the tags property or tags class And now let's add a tag in here. So let's add an href. Nope, that's not at all what I wanted to do. href is equal to, we're gonna leave that blank for now, and we just want the tag in here. So let's do this. tag refresh our browser and we can see that we've got two links in here with tag one and the word. Now we actually just need to get that link so let's go ahead and put these on separate lines. Yes, this is ugly, but for our purposes right now, that's actually okay because we don't really have a design to go off of, so uh that's totally fine in my opinion. What we're going to do is we're going to add yeah, I had that backwards there.

10:09

Line break at the end, and we're going to add the tag, but what if we wanted the tag link? Well we could do tag . URL is what you think it would be, but it doesn't have a URL to a page. That's a Wagtail thing. We want tag. slug. And here we can see. Multiple worded tag, multiple worded tag, tag one, tag one, so it has a slug in there, it just happens to be it's the text but slugified Now that's cool and everything, but just clicking this link is not going to do anything. We want to go to some sort of probably a blog listing page that filters out all of our blog posts. So if we click on, where were we? Blog article page. Or custom blog article page title.

10:55

That's what it was called. And we go back to our listing page, so our super cool blog. And we don't see any tags in here, that's totally cool, but we do have categories. And when we click a category, we can filter through these categories. And in the URL where it says category is equal to, what we're going to add is tag is equal to. And this tag is going to be Tag dash one. Now if you're wondering where I got that from, I'm just going to reopen that page. And you can see in here where it says tag one and the tag itself or the slug was tag one. That's where I'm getting that from. So we need to go into our template and create a URL. Now I'm going to take a shortcut here and I'm just going to hard code blog in because You could be spending a lot of time watching me do a lot of things that are covered in other videos, but instead I'm just gonna take a shortcut, go straight to blog, and the tag is going to be

11:47

tag. slug. And now when I refresh, I'll click this, and you can see that this goes to tag is equal to tag one. I can close that other page. Now this should be filtering, but it currently doesn't, so let's go ahead and make that filter We need to find our blog listing page. There you are. And where is the context? So we want to do the page stuff. That's cool. We want to filter all of our posts. So what we want to do here is we want to take all of our posts. It's equal to all posts. I'll make that a little bit bigger, make some space for us to see that. All posts. filter, and then we do

12:34

tags slug is in, and we give it a list. And for example, we could do tag one in here. And where I'm getting tags from is in our blog detail page. So we've got all posts referring back to our blog detail page. objects. live dot public, does a bunch of stuff in there, reorders everything. If we go to the blog detail page We have tags. Tags goes to our through model or through table called blog page tags. Let's check that one out. And in here, we know that there is definitely a slug because we have that being accessed in the template, and we can simply match that slug. So I'm gonna save that. Everything looks okay in our terminal, and let's do a quick refresh. Alright, so we load up our browser and we see, oh no, nothing shows up.

13:23

Why is that? Well, let's take a look here. I probably made a mistake somewhere. And uh yeah, that's exactly what I did is I made a mistake, and it happens, and just so you know, it's okay to make mistakes. What I was looking for here was tags slug is equal to exactly this. And that's taking a list, and that's not exactly what we want. What we want to say is Look for the tags where the slug is in this particular list. So if there were more than one tag in here, we could pass it a list and we could filter through that all at the same time. Now I'm gonna save that. And refresh and this will work. Here we go. Custom blog page title. And let's go back to our blog and add tag one somewhere else. Let's add it to blog post number one.

14:11

And I'm gonna type T A and it will autofill for me, which is really nice. Thank you, Wagtail, for doing that. Click that. Publish, and I'm going to refresh my blog listing page. And now we have two. And now this is filtering by a particular tag, which is really, really nice. Now if you don't have any blog post to show for instance. Let's just type in test there. That's still gonna show everything. And again, I was getting a little ahead of myself. That's because we hard coded tag one in here. That was a silly thing to do. So what we want to do here is we want to add a conditional. So if request. get. get tag Or none as the default. So if there is a tag in here, simply grab that tag and I'm going to copy this, make a new variable.

14:57

Tags is equal to. And we 're gonna throw that in here. Now this is not a very elegant way of doing this, but it is a way of doing this. Everything looks good in the terminal. Refresh. Nothing shows up. Perfect. Type in tag one again. Everything shows up. Let's click on multi-worded tag. We know that there's only one blog post that has this one. And voila! Just like that, we have added tags to our blog listing page, in a sense, and our blog detail pages. And the last thing we need to make sure we do is for our video blog page we're going to see that there are no tags in here. We simply need to go and add a field panel. Where are you?

15:45

Uh is it blog video page? Blog video. What do I call it? Video blog page. There you are. And so it's already inheriting from the blog detail page, and the blog detail page already has tags. So all we have to do is say, hey, put those tags in our page. Refresh, and there we go, we see tags. And type in tag one, it'll autofill. If I type in mu, multi-word a tag, helps me autofill, and I can publish. Obviously, I'm gonna need other other content in there for this to work, but essentially that's all there is to adding tags. So as a quick summary, here's what we did. We added a new class called blog page tag and all that really was was a parental key to a particular page

16:32

and if you don't want to use it in your blog detail page you can use it on any page, just change this name out. The related name is going to be tagged items, and our tags is simply a cluster tagble manager with a through table. And blank is true because we didn't want people to absolutely have to fill out tags every single time. So that's how we got away with that. Uh and then, and then if I scroll on down, we can see. I added a field tag with the tags property, and then in our template, we simply loop through it. For tag and page. tegs. all, don't forget the dot all, and we can access tag. slug and the tag name itself I'm going to clean this code up just a little bit right after the video and the commit will be nice and clean for you. And one last thing.

17:18

Because there was code in this video, you can see all the code that I've written. I have it in a GitHub repository. I will leave that link in the description down below. Hey there, my name is Caleb Tallin. I am a Wagtail Core team member, and I just wanted to say thank you for watching this video about learning Wagtail CMS. If you enjoyed this video, there are plenty more where this came from, all available on learnwagtail. com. And if you would like to dive into any particular subject, you can always go to docs. wagtail. io, the documentation. is quite fantastic and in true Python fashion it is very well written. And hey if you like this video don't forget you can always share, subscribe, or leave a comment down below. I love hearing from the community. And I look forward to seeing you in the next video

Questions this talk answers

How do I add tags to a Wagtail page model?

Create a through model inheriting from `TaggedItemBase`, link it to the target page with a parental key, and add a `ClusterTaggableManager` with that model as its through table. Set `blank=True` if tags should be optional.

Discussed at 1:33

Why aren’t my Wagtail tags appearing in the page editor?

Wagtail only displays model fields that are included in an edit panel, so add a `FieldPanel` for the `tags` property. If a child page overrides the parent’s content panels, include the tags panel in the child page as well.

Discussed at 5:27

How do I fix the “no such table” error after adding Wagtail tags?

Create and apply database migrations with `python manage.py makemigrations` followed by `python manage.py migrate`, then restart or refresh the server.

Discussed at 7:01

How do I display Wagtail tags in a page template?

Loop over the page’s tags with `for tag in page.tags.all`, then render the tag name or its `slug`. The `.all` call is required to iterate through the related tags.

Discussed at 7:48

How do I filter a Wagtail blog listing by a tag?

Read the tag from the request query string and filter the posts through their related tags, matching the tag slug. The tag links can point to the listing page with a query such as `?tag=tag-one`; use the requested value rather than a hard-coded slug.

Discussed at 11:47

Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.

More videos from Wagtail CMS