Video Tour of Wagtail 8.0
Published August 26, 2026
This video is from Wagtail CMS 2019 .
Pagination is the ability to click through "pages". You most commonly see this on a Blog Listing Page, where you have "page 1 of 4" for example. In this lesson we're going to use Django Paginator right out of the box to add pagination to our Wagtail Blog Listing Page. No 3rd party packages, no craziness, and minimal maintenance. Just beautiful Wagtail and Django working together in 11 lines of code in our Wagtail Page Pagination.
Tutorial: https://learnwagtail.com/tutorials/how-to-paginate-your-wagtail-pages/
The Git Commit: https://github.com/CodingForEverybody/learn-wagtail/commit/409c23e3d550cbf03db5201f7bb27d2cef4318f2
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 #Wagtail #Django #Python
Automatically transcribed, so expect mistakes in names and technical terms.
Hello, welcome back to another lesson on learning Wagtail. In the last few lessons we learned all about specific and parent classes and child classes and subclassing and all that stuff. In this lesson, we're not going to be talking about any of that. We're going to do a little bit of a 180 here, and we are going to talk about pagination. Now there are all sorts of packages out there that you can install. And it will do a bunch of pagination for you. But in my opinion, I find that amount of work versus adding your own pagination. ends up being roughly the same anyways, so you might as well have your own pagination using Django's using Django Core Paginator. And that's what we're going to do in this lesson. So this lesson takes place on our blogmodels. py
and we want to find our listing page. Now, our listing page, this is where our pagination is going to live, and it's going to live inside of our get context. Because the idea is that with all of these posts that we have here Let's say there are 20 posts and we only want to show five per page. Well we need to split that up into four different pages and then return that back to our context so that our template can have access to it. Now if that sounds complicated, I actually guarantee you that it's not that complicated. And in fact, we can probably get this up and running in 10, maybe 15 minutes. So the first thing you're going to need to do is import your paginator. And there are a lot of docs on this already, so if you're not familiar.
With Django pagination, that's totally cool. There's a lot of documentation on it, or you can just watch this video and we'll go through it together. So you go from Django. core. paginator, import, and we're going to import three things here. Empty page. Because what happens if there's nothing on a page? We want to import page not integer, because well we're gonna get into this one a little bit later, so I won't explain that now. And we're also going to import the main one called paginator. And so I'm gonna scroll on down to my blog listing page right in the context here. Where is my context? There it is. And I'm going to rewrite some of this stuff. I'm going to leave the post will have child pages in here, so you need to use .
specific in your template because that's still going to be true. But I'm going to get rid of context posts. We're going to add this in a little bit later. And let's call this. All posts. So this is a local variable now. This is scoped to this function and it's not going to be returned in the context. Not yet, anyways. And let's also do a quick order by. So let's order by first published at And this is a Wagtail field that when the page is first published, it gets a little update and just says this is when it was first updated first updated. So drafts. Draft pages don't have a first published at, and if you republish the page, that does not affect this particular field.
So we can rely on this one to order the way we want it to order. Next, uh we actually want to add our paginator in here, so I'm going to scroll this down and maybe make this a tad bigger. So let's add our paginator in here. Paginator, this is a variable, and we're going to instantiate the paginator class. All posts, and how many posts do we want per page? Well, we usually want somewhere around five to ten, maybe somewhere around there. But because we only have four posts in this lesson. And maybe what I'll do in a few minutes is go and make more posts. I'm actually just going to say one. One post per page so that we can get the paginator up and running and then we can change this. And typically what I do is I leave a little to-do note in here for me. And this just says change to five per page. Something like that.
And so that's just a reminder for me. So later when I come back to this and I say, oh, there's only one post here. Or when I'm about to commit and I do uh a git diff on on the different files, I can actually see in here that says to do change to five per page. And so before I commit, it changes it. So little practice that I do. Feel free to adopt it, feel free to ignore it. Next we need to do a little try accept. Um, but I guess first we need to figure out what the page is. So the page is going to be request. get dot get page. And so what this is going to do is simply add a page is equal to one or a page is equal to two kind of thing into your blog. Okay, next we need to create a try and accept. So I'm gonna hash this out first. Try accept
this is going to accept a uh page not an integer And also a final except for empty page. We'll talk about those in just a moment. In the try, what we're going to do here is we're going to say post is equal to paginator, paginator dot page page. And so this is going to try to get that page. If this page does not exist, it's going to throw some sort of exception. Now if the page does exist, let's say someone typed in question mark page is equal to ABC123. That's not an integer. Page is not an integer, so this is going to catch that. This is going to say this is a string. And basically it cannot convert from a string to an integer, so we catch it in here. And what we're going to do here is say, if the page is not an integer, just by default, just return the first page.
So paginator or post is equal to paginator. page first page. That is it. And then lastly, we want to check to see if the page is basically out of range. So uh what happens if someone goes to Question mark page is equal to 999999999. Well, that page doesn't exist. There is nothing to show on it, and so that would return an empty page. And in this empty page, we need to return something So instead of delivering or returning the very first page, what we're going to do is we're going to return the very last page. And this happens with posts. is equal to paginator dot page, paginator dot numpages. And you can see that this is actually being autofilled for me here.
That's pretty nice And that is almost it. There's not too much more that we have to do except throw this into the context. So I'm gonna scroll this down again and let's go. Context. We're gonna add a new one in here called posts. It's actually not new. It's just renamed from earlier. And post is equal to posts. So let's open up our browser. Let's head on over to our blog page. And here we can see we just have the one post. Now if you're just coming into this video, you're probably thinking, oh, what the why why is there only one post? Well, let's just Get rid of any sort of doubt there, and we're going to say just temporarily that posts, the context posts, is equal to every blog page that's live, that's public, and it's going to order by its first published date
And when I refresh our page here, we're going to see that eventually, when it loads, there's going to be a bunch of different blog posts. So here we have four blog posts. So there are in fact four blog posts. But when we do the paginator, it only shows up with the one. So what we need to do now is we need to go into our template and we need to Make sure that this is looping through the posts properly, which we know that it currently is from prior lesson, but I'll go over that anyways. And we need to add our paginator. So let's open up our blog, our blog listing page. And four posts and posts. So we know that this loop is already working And at the bottom here I'm going to add my pagination.
So I'm just gonna make some space to work here and I'm gonna add a new container and I'm gonna add a row in here Coal large I don't know 12. I don't really care what that is right now because this is not a bootstrap course, this is a Wagtail course. Now what we need to do in here is we need to add our pagination. So I'm gonna make some more space in here so that we can somewhat forget about all that extra stuff that we were just typing, the container row and column. I'm going to say if post dot paginator. num pages is greater than one. So if there's more than one page, then show the pagination. If there is no pages to show, if there's no additional pages, so everything fits on one page.
Why show pagination at all? So that's just a little thing that I like to do. Hide pagination if it's not necessary. In fact, we could wrap this around this entire section. We're not going to, but that would be a better practice. Then in here we want to add just pagination, I believe it is, in bootstrap. And let's sort of hash this out quickly. So let's do uh we've got an ally. page item. And in each page item we're going to have some sort of link. So a dot page link tab and it autocompletes for me And let's put a number in here. Or a number sign rather. And let's make a few of these.
And see what that looks like. So we have some sort of pagination buttoning in here. So that that's okay. That means our pagination, our styling is working So our first one what we want to do here is we want to check to see if there are any previous posts. If if you're on page two, should there be a little back button? I think there should be. Some people don't like that. That's up to you, but we're gonna sh we're gonna go over how to do that anyways. So around this ally, this list item, what we're going to do is. If posts dot has previous and if And change that indenting. And in here what we can do is span and let's do uh some HTML entity.
L-A-Q -U -O. Make some space in here, make some space in there, and let's also do the last one as well. So the last one, if you haven't guessed it already, is if post. hasnext And if change our indentation, because we are Python developers and we love indentation. And this one is going to be an HTML entity as well. So let's put this one in a span and are a rackwoo. Something like that. So let's save this and make sure that that is working. So we're currently on page one, so there's no page zero, and this is going to show us that there is another page.
We could possibly move forward. Now we don't have links in there yet. We'll add those in just a little bit. Let's add the middle section now. The middle section is page one, two, three, four, five, and so on and so on. So we need to add a for loop in here for page num in post dot paginator paginator dot page range And four. And let's fill this one with the page num. So page underscore num because that's what we're looping through in here. And let's refresh and we'll see pagination one, two, three, four. So we're paginating one one blog post per page. We have four blog posts, so we have four pages. That makes sense. Now we just need to go in and add our links.
And this is actually super easy too. So let's add our first one, our back a page link. So that back page link is going to be question mark page is equal to Post. previous page not preview previous page number. If you're asking Caleb, where did you where did you get this question mark page from? I got the question mark page from in here. So this is just grabbing the page out of the URL from a GET request. So if someone goes to your site. com slash blog question mark page is equal to three, it's automatically going to go to page number three If you wanted to change that from page to just p, you could do that as well. And you just have to change this to p.
So nice and simple. Typically people stick with page. And let's add the moving forward link. The moving forward link is going to be question mark page is equal to post. nextPageNumber. So these are all very predictable variable names at this point. And when I click this, this is going to go to page two and page three and page four. But there's no more after page four, so that one hides. I can go back a page, page three. Page two. And page one doesn't have a backlink because there is no page zero. Now we just need to fill these middle ones in here. So let's go ahead and fill these middle ones. And this one is going to be page is equal to simply page number, page
num. So that people can jump around Refresh, and let's jump to page three. We can see up in the URL it's changed to page three. Change to page two. There it is, page two. So we have pagination actually working at this point. That's pretty nice. There's one more thing I want to add in here. What happens? What happens if you go to page three? You don't really know that you're on page three. There's no visual cue for your brain to say, oh, I'm on page three. So you could either spell it out for someone, you could say you are currently on page. Page num, you could put that in some sort of header or some sort of title somewhere. Or you could make this this list item actually sort of highlight a little bit. So let's go ahead and do that. I like that one better. It just seems a little more nonchalant, a little nicer to work with.
If your page num is equal to post. number, And let's add a class called active and if. And there we go. We're on page three. We're on page four. We're on page one. We're on page two. We're on page three. We're on page four. We have pagination, everybody! So I did say earlier in this video what I was going to do was make a bunch of new blog posts, but I think actually this point has been driven home. I think this is pretty clear so far, so I don't need to go and make those additional blog posts. However, what I'm going to do is instead of changing this from 1 to 5, I'm going to change this from 1 to 2. I'm going to go ahead and get rid of that. And so we're going to see two blog posts per page.
And so when I go to page four, there is no page four. And you can see that it's actually just gotten the last page, the last two results here for page four because it doesn't exist. What happens if I go to page a bunch of letters? I'm not gonna try to say that. Uh is gonna go to page one, which it did. It went to page one. So that's really, really nice. And again, we can just skip around. And we have two blog posts per page, and all we did was change a single integer in here. Just like that. So what we did in this lesson was we created our own pagination using Django Core 's paginator We did not have to use any third party packages or tools or anything like that. And the logic was actually super simple
The hardest part about adding your pagination is just remembering that it's like post. hasprevious, or post. hasnext, or for instance looping through all your page numbers in post. paginator dot page range. Like just remembering those things is a little bit trickier. But honestly, you could just check out this repo at any point in time, because I'm gonna leave this in here, and you will have a permanent reference to always take a look at So that's all there is for paginating. I hope you learned a thing. If you did, if you like this video, don't forget you can subscribe, you can share, you can comment. And as always, my name is Caleb Tollin. I am the voice behind the video. If you like this video, I have plenty of other tutorials available at learnwagtail.
com. And if you aren't really happy with the way that I explained Paginator in this video, Django has fantastic documentation on this. You can always check out Django's documentation on their paginator.
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.
Published August 26, 2026
Published July 9, 2026
Published May 20, 2026
Published April 16, 2026
Published March 12, 2026
Published March 10, 2026