Video Tour of Wagtail 8.0
Published September 19, 2026
This video is from Wagtail CMS 2023 .
Every modern website has a banner. Often it comes with a title, subtitle, an image and an optional call to action button. In this tutorial we're going to add all 4 of those to our Home Page. Includes: Wagtail Images, ForeignKeys, ImageChooserPanel, PageChooserPanel and installing Bootstrap 4 as the frontend framework.
Full code for this (including code for part 2) can be found here:
https://learnwagtail.com/tutorials/adding-banner-part-1/
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
The speaker extends a Wagtail homepage model with a rich-text banner subtitle, an optional banner image linked to Wagtail’s image model, and an optional call-to-action linked to another Wagtail page. They explain the relevant Django relationships, migration choices, deletion behavior, and how to run migrations when existing homepage data lacks the new fields. They then add Wagtail field panels, an image chooser, and a page chooser so editors can manage the new fields in the admin, and populate example homepage content; the visual HTML and CSS rendering are left for a later lesson.
Summarised automatically from the transcript.
Automatically transcribed, so expect mistakes in names and technical terms.
Hello and welcome back. In today's lesson, we're going to be adding a little bit more to our homepage because right now our homepage is. . it's pretty boring. What we're going to add is an image, so a banner image. We already have a banner title. We're going to add a banner subtitle, and we are also going to add a call to action button And then if we have time in this video, we are going to also get this rolling with bootstrap 4. This may be in this video, this might be in the next video, so take that last chunk with uh a grain of salt. But eventually we're going to be getting this website up and running with Bootstrap 4 anyways. So here you can see we have class homepage Doc string that says homepage model. We've got uh
template to use. Not templates, template. We've got a max count is equal to one, so there can only ever be one homepage And we have a banner title. And if we actually go into uh our browser, Firefox, Chrome, Edge, Safari, whatever your browser of choice is. For this video, mine is Firefox and I open up localhost 8000. I can see Hello, this is a banner title. A big white boring screen. Now this is pretty bland. So let's uh let's dive right into this. And uh yeah, let's let's add some some goodness to this. Let's add uh Jumbotron, if you will So as you can see, we already have our banner title. We don't really need to do anything beyond this, uh, but let's go ahead and add a banner subtitle and let's make this rich text so that
The content editors of our site, the content entry people, can put in bold or italics as well. And Wagtail will automatically take care of that for us. So as with everything, uh for our rich text field, we need to import this. And so let's go from Wagtail. core. fields import rich text field And now down in line 15 we can say banner subtitle is equal to rich text field. And this has a nice little keyword parameter in here, a keyword argument called features and this takes a list and this is a list of all the features that we want to have available for this rich text editor. So we can actually edit
the available features in our WYSIWYG By simply giving it a list of I guess features that we want. So let's create that list and we'll say bold and italic. And now we want an image. Images are their own little beast in Wagtail. Wagtail has special ways of dealing with images, so we don't just have an image field. What we use is a foreign key to Wagtail's existing image model. So let's go ahead and create that foreign key now. Let's call this banner image is equal to models dot foreign key And as the first parameter, we want to type in which model we're trying to access. Now we're going to lazy load this by wrapping the name of the image model in a string.
So we need the app name, but we also need the class name of the image. Now I know off the top of my head that it's Wagtailimages. image. I've used this a lot, and you can also reference this at any point in time in the Wagtail documentation. It's all over the place in there Next we need to set a couple fields. Now we need to set a couple fields. So one of them is null is equal to something, blank is equal to something OnDelete is equal to something, and we also want a related name is equal to something Now I haven't filled any of these out, so let's quickly talk about these. Null, I'm going to set this to true, and the only reason I'm setting this to true is because homepage already exists. Now when you make your migrations and if this was set to false.
. And this page already exists, your migrations will say, oh, what should that default value be? Now we don't actually know what that value is going to be right now. So I'm going to set this to true. There are obviously ways around this, but for simplicity and to keep the simple nice and focused on just adding a battery title, um, subtitle image, and a call to action. We're not going to get into the nitty-gritty of migrations right now. But let's say someone goes to edit this page and they are prompted with, oh, should there be a banner image? We say yes, there should be a banner image. Which means essentially when the form validates, can that field be blank? And we're gonna say no, it cannot be blank. It's false. On delete models
dot Set null. Now the reason we do that is because uh we don't want essentially when this image is deleted or when this page is deleted, we don't want anything to cascade, we don't want anything else to delete We simply just want to set this to null. And the related name is plus because we are not going to be using any sort of special related name with this. We're actually just going to use the field name. And lastly, let's add a link. I'm gonna scroll down here. Let's add a link, a call to action. So let's call this Banner CTA is equal to models. Now this is going to be a link to another page, not necessarily a URL. If you wanted, you could however just use a
models. url field and then you can put in any sort of URL that you want and that could go to any external website that you're looking for That's not what we want here. What we want in this instance is we want to be able to link to another Wagtail page, which we have not created yet, but we want to create uh a link to another Wagtail page we want Wagtail to automatically connect those dots for us. So we're going to use another foreign key. This time, the string that we use is Wagtailcore. page. And this is just where pages come in So Wagtail Core is the app name and page is the model name. Now we can get that by looking at this.
So we've got Wagtail. core, so WagtailCore is the app name. This just happens to be the path. And page is the class name. And essentially that's what we've done here. We've said the same thing. So now we need to copy and paste these. Or type them out if you're brand new because it's better practice. Uh null. Can this be null in the database? Yes, it can be. The button is optional. Can it be blank Yes, because this button is optional. On delete, what do we do? We're just gonna set this to null because again this is optional. Now I'm going to save this and I'm going to open up my terminal. And the reason I opened up my terminal is because I want to see if there are any errors at all. And it doesn't look like there are any errors. Now if you're opening up your terminal and your site is not running, or you go to localhost 8000 and your site is not running, remember all you have to do.
is open up your terminal and type in Python3 manage. py run server Or Python with a Y. Manage. py run server, there we go, it's running on 127. 001 port 8000, aka localhost 8000. Refresh my page, and this is good news. So this shows us an error. And this is saying no such column. Basically this is saying in the database there's no column called banner subtitle in the table called home home page. Now if you're ever looking at this and you're scratching your head and you're going, well, why is that? Well, this right here is just regular Django. This is a migration that needs to be done.
So this is saying basically it cannot find banner subtitle because it does not exist in the database. So let's go make it exist in the database. Open up your terminal. cancel, python 3, manage. py, make migrations. Now what this is saying here is you're trying to add a non-nullable field banner subtitle to homepage without a default So if we look back at our code in here, we didn't say that this can be null or blank, so by default, Django is saying, oh, this is a required field. Okay. So because this is the home page and this page should have a little more pizzazz, I guess, uh let's make sure that this subtitle always exists. So go back to your terminal. Or I'm gonna go back to my terminal
and it gives me two options. Option one, I can provide a one-off default now, which will just automatically fill the rows or fill that subtitle with whatever I put in here. Or two, I can quit and I can adjust my models. py. Well, I'm gonna select number one. And I'm just going to say test text. Nothing fancy. And I can edit this on the page. This is just going to fill out my information on the page for me. Cool beans, okay. Python 3 manage. py and instead of make migrations I'm going to migrate. Things are looking good. Python 3 manage. py run server. And when I refresh my page, things start working again.
Now we're going to want to go into our admin and we're going to edit this page. Now when we edit this page, we're not going to see anything that we've added yet. All we have is our banner title which we added from the previous lesson and a title which comes default with every Wagtail page. Now here's where it gets A little bit gray, a little blurry between what is Wagtail and what is Django. Well, right here, for the most part, this is Django. This one here is Wagtail. And we know that because anything from Django. db. models is, well that's Django. It's it says it right in the name from Django. Anything from Wagtail is Wagtail. So these fields don't show up right now and that's because we have to add these fields. So let's go ahead and add these fields. Now we're going to use a couple different field panels in here.
And we're also going to wrap this in a multi-field panel. So I'm going to take this step by step. First, let's add our field panels. So we have a banner title. Let's also add a field panel for banner subtitle. We also need to be able to select an image. Now instead of just being uh boring and saying, oh, here's an image upload field, Wagtail actually says, oh, I'm gonna wrap this around a bunch of nice UI stuff. And all you have to do to invoke that is use the image chooser panel. And you put the name of your image field in there. So in this case it's banner image. And then lastly, we want to add a page chooser panel. And this one is for our banner CTA. Now the page chooser panel basically is
very, very similar to your image chooser panel, and we're going to actually and we're going to visually see these uh in just a little bit. But right now if we save this, we're going to get an error. It's going to say that image chooser panel and page chooser panel are not well they're not imported and that would be correct, so let's go ahead and import those Now you think they would come from field panel, and in fact page chooser panel does. Page chooser panel comes from Wagtail Admin Edit Handlers. Now the image chooser panel is a little bit different, uh, and by a little bit I mean very tiny. So instead of uh from Wagtail. admin, we want from Wagtail dot images. Edit Handlers Import Image Chooser Panel.
Let's save that and check out our console or our terminal And see it was complaining that image chooser panel is not defined. Basically it's not imported, but now it's saying that oh everything looks good. Now if you boot up your browser. Again, whichever browser you want, we're going to see a couple different options here. So now we've got our banner title, we've got a banner subtitle, we have a banner image, and this is what the image chooser panel does. Choose an image. It allows you to visually select an image or upload an image on the spot. Now the page chooser panel, which is our banner CTA, lets you choose any page that you want. Now we only have the one page, we only have home, so we're going to select that one. It's optional, so really selecting the page as itself doesn't make a lot of sense for the user to click a button just to go to that same page.
So let's actually clear that out. And let's add an image in here. And I'm just going to add any image and I'm just going to pray that whatever image I decide to use is uh somewhat acceptable. Facebook, that seems safe Oh look, that's actually perfect. Learnwagtail. com. Okay, uh that test text that we added when we made that migration is default in here. Remember in our rich text field where we said, oh, we just want bold and italic? Well we have bold and italic and that's it. But because it's a WYSIWYG, you also get the paragraph element. So anytime you add one line here, another line here, it's going to create new paragraphs. So just A quick note about that. So let's sort of design this page a little bit. We have a banner title and let's call this
um well I guess at this point we need to decide what this site is about. Now you might be taking this this course or watching these videos to make a new blog or to make a learning management system. Maybe you want to learn Wagtail to get a new job. I don't know what your scenario is. So unfortunately I can't tailor all these videos to your exact purpose. If I could, I would. So I'm going to try to keep this very generic This site is going to be basically a blog site for let's say a startup. And so we're going to have team members eventually. We're going to have blog detail pages, a blog listing page. We're going to have a contact page and all sorts of stuff. So let's just assume we're a startup, but right now we just need a home page, maybe a flexible page, like some sort of basic type of page.
We also want a blog listing page and blog detail pages. And that's it. And then we will extend it from there afterwards. So this is going to be our homepage. Now we need a name for the startup. So let's just call this welcome to Startup life, I guess. Uh banner subtitle is a required field you can tell by that little asterisk in there. We help startups start up. We have a banner image and we need a Banner CTA, which we're going to leave out of this for now. Actually, this is a tricky one. Okay, we're gonna come back to this one probably in a later lesson. I'm gonna publish this, and when I open this page up in
Another tab. I go into localhost 8000. Again, nothing changes. Now the reason for that is because we haven't changed anything on our homepage. So what we need to do is we need to add some actual content here, some some actual HTML, a little bit of CSS. We're gonna make this website actually start looking like a website.
Use a foreign key to Wagtail’s built-in `wagtailimages.Image` model rather than a normal image field. The example allows the database value to be null, prevents the form field from being blank, and uses `SET_NULL` when the image is deleted.
Discussed at 2:22Define the CTA as a foreign key to `wagtailcore.Page`, with null and blank allowed because the button is optional, and use `SET_NULL` on deletion. A URL field would be appropriate only if the link needed to point to an arbitrary external URL.
Discussed at 5:28Run `makemigrations`, provide a one-off default for the new non-nullable field when prompted, then run `migrate`. The migration updates the existing homepage row so the site can load again.
Discussed at 7:51Note: 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 September 19, 2026
Published July 9, 2026
Published May 20, 2026
Published April 16, 2026
Published April 1, 2026
Published March 10, 2026