Video Tour of Wagtail 8.0
Published September 19, 2026
This video is from Wagtail CMS 2023 .
Contact forms are an essential part of many websites. Wagtail CMS gives us a lot of powerful features without having to do very much work. In this tutorial we're going to create a contact form and a contact form landing page. The contact form will have 3 completely custom fields that the content editor can manage on their own. We'll also explore the Forms feature in the Wagtail Admin.
Tutorial: https://learnwagtail.com/tutorials/contact-forms/
The Git Commit: https://github.com/CodingForEverybody/learn-wagtail/commit/d67bc5659ca023c21d8a9f01d30604f6d67cd2d6
Used in this video: Wagtail 2.4, Python 3.7, Django 2.1.5 #Wagtail #Django #Python
Wagtail’s built-in form support can be used to create a dedicated email contact-form page with configurable fields, introductory and thank-you text, and email settings. The implementation defines a contact page and related form-field model using `AbstractEmailForm` and `AbstractFormField`, then builds templates for the form and its post-submission landing page. Submitted data is available in Wagtail’s Forms admin, where it can be reviewed, filtered, deleted, or downloaded as CSV; the speaker also warns that the basic form is unprotected and should use a CAPTCHA such as reCAPTCHA in production.
Summarised automatically from the transcript.
Automatically transcribed, so expect mistakes in names and technical terms.
Hello, welcome back to another lesson on learning Wagtail. In this video, we're going to be talking about forms, specifically contact forms. So a form that's going to email you. Now Wagtail does a lot of really nice things with their forms, and if you're thinking that you might maybe learn a little bit about Django forms, unfortunately you're not going to. These forms take Django forms to the next level And it's really going to change the way that you want to deal with forms in the future. It's really, really nice. There's a lot of good things that come right out of the box with Wagtail Forms. And so without further ado, let's go ahead and jump in So I'm going to CD into my website and pip env shell just to get inside of it. And I want to start a brand new app.
So Python. Python 3, manage. py, or just Python if you're already defaulting to Python 3. Start app, and I'm going to call this one contact. And that's all that takes. And then Python 3 manage. py run server Alright, so this port is already in use, so what I'm going to do is python3 manage. py runserver 0000800 and 1 Changing it up, changing it up. And then if you open up your browser and go to localhost 8000 and one slash admin You will see your admin. And let's make that regular size again. So we want to create a new page type entirely
where it's just a contact form. So this form is not going to live in the global context. It's not going to be accessible on every single page. It is a single page form. We started an app called Contact and the next thing we need to do is we need to open up our editor and go into base. py and look for this line right here. It went straight to it for me. Wagtail. contrib. forms. We need to make sure that is enabled. So if that is not enabled Please make sure that is enabled. Make sure that's inside of your installed apps. And we also want to add our new contact app Save that and I'm gonna close that down because we don't need base. py anymore.
Next we're going to open up our new app. Where is our new app? Contact, it's the green one. And let's go into models. And I'm going to create a new model here. Actually, I'm going to create a few different models. So I'm going to create one is going to be a form field. Make that a little bit bigger. There we go. Form field, one's going to be a form page. And when I said there is a few, I actually just meant there is a couple. We're going to create a form field and a form page. So let's start off with our form field. Now our form field is actually going to give our page a bunch of different fields that Wegtail will automatically populate. It will make it nice for us to work with. It will just automatically do a lot of things for us. And this one is going to inherit from abstract.
Form field. We have not imported that yet, so we're going to need to do that in a little bit. And this one is just simply going to take a parental key, so it doesn't really matter what you call this variable. I'm going to call it Page and this is going to be parental key and this is going to go to form page, which is not existent yet, but will be shortly. And I'm gonna make this multiple lines For readability purposes, onDelete, now not a string, onDelete is equal to models. cascade. We want to cascade these. Uh related name. Your related name, what do we want to call this? Well in the docs, I believe it's just called form field, so uh let's just follow the docs And that's all there is to this one. We don't need any panels or anything because we're going to be using related names in
a different class. And that class is called form page. So let's create this class form page. And because we want this to be an email form, we're going to inherit from abstract email form. So as always, I like to be explicit and give this uh a template path uh so that I can find the template easier. Uh the reason that I like to do that, I know I've mentioned this before, is just because if anybody else is coming into this video right now or they They clone this entire repo. They can automatically go into contact models. py, open up their form page class, and they can see exactly where that's supposed to live. So there's no hunting around for it. It's nice and easy. It's right in your face. And we're going to throw this into contact slash
contact page dot html. And maybe Because there's a little bit of a naming conflict here. What I should have done was I should have called this app Form or something, but I didn't want to get too confusing with the word form. So maybe let's change this one from form page to contact page. And next we want to add a little bit of a little bit of context to our page. So when you're on this form page, is it just simply a form? Well, no, there's usually some Some sort of text saying, hey, contact us this way. Here's a phone number. Here's some other things that you can do. We're going to add that in the form of rich text. So we're gonna add a rich text field and blank is going to be true.
We want that to be optional, I guess, in case you want a contact form that doesn't have any sort of context context or content on it rather And then we want to create one more, and we're going to call this one thank you text. And this is for when somebody submits your form, you're going to see this text instead of this text. Alright, let's jump down into our panels. We know that everything in Wagtail, if you want it to be editable, has to basically come in the form of panels. Not panels. This one is actually content panels because it's a page. And we are going to take abstract email form. content content panels and then we are going to add a list to it Now this list is going to have a field panel of
intro and uh that's fine intro. Give that a comma, give that a comma, comma, comma, chameleon. Let's also put in thank you text. Thank you text without brackets. And let's put one in between here. Let's put an inline panel. So we're going to add an inline panel for our related name. That's our form fields that we want to use. Inline panels always use a related name So let's do form fields. And let's give this one a label. Form fields. Okay, so this next part we're going to add a multi-field panel where we can have multiple fields in one sort of section and one sort of panel in the Wagtel admin.
But we're also going to add a field row panel, which I don't recall off the top of my head if we have learned about this one or not yet. But a field row panel. Basically lets us split up entire columns. So if you if you think of bootstrap or if you think of a grid layout on a front end and you know how you've got like one column all the way through column 12, and you can have like three out of twelve or something like that. That's what we're going to do here. We're going to actually split this up. We're going to put the to address and the from address and we're going to say, oh, actually only be half the width each so that the forms are side by side or the fields are side by side And I'll show you what that looks like once we actually get into the admin. So let's add a field panel in here and call this from address. And we're going to give this a class name of col
6. I'm going to copy that down and change this to to address. Again, that class name is going to be call 6, so 6 out of 12 columns. And then lastly, I have one more in here, a field, field panel. And this one is going to take subject. So if you're wondering at this point, Caleb, where does subject or from address or to address, where do these come from? Because we only really said intro and thank you text. We didn't say we wanted anything else. Well, if I jump to this definition here, which can't even be found at this moment for some reason, you will actually see that there are fields in there called
From address to address and subject. And in fact, I actually put that in the wrong spot. Let's bump that over. Let's add a comma there. And let's give this a name. A heading Uh email settings. That's what we'll call this. Email settings. Okay, let's save that. And you're going to instantly see that there's all these underlines. Basically, my system is saying. Oh, you don't actually have rich text or field panel or multi-field panel or field row panel or any of these other things. Caleb, you need these things. Absolutely we do. So let's go ahead. And do some imports here. We already use models. We can see that on line eight, we already use
models. So let's go ahead and import everything else that we need right now. So let's add our parental key. Let's do from modelcluster dot fields import parental key. Okay, so that one goes away. Let's get rid of that line so that the class and this class, the squiggly lines underneath the classes aren't complaining. Thank you, Flake 8. Let's import some field panels. Or I guess they're just called panels rather. From Wagtail. admin. edit handlers import. And we want to import a field panel. We also added a field row panel. We added an inline panel and I think the only other one we used was a multi-field panel.
No more errors in there. So next we need abstract email form. We need our rich text field and we need abstract form field. Let's grab that rich text one now. So let's do from wagtail. core. fields. That was a weird way of spelling fields. Import rich text field. And Lastly, the important ones for this lesson anyways come from wagtail. contrib. forms. models, import. Abstract email form and abstract form field that one. New line, any flake eight errors, doesn't look like it, there's no errors down here.
Let's open up our terminal. Contact page has no attribute called form fields. What did we call it? Related name, form fields. Contact page has no attribute form fields. Let's take a look at this again. Uh related name, form fields. Everything is good. Oh, there it is, right there. It's not form page anymore, it's contact page. This is our model that we're trying to connect to. That looks nice nice and healthy. So when I refresh my page here. Nothing's gonna happen and what I want to do is open up home, add a child page, and here we can see we've got contact page
And now this does some really nice things for us. So it has a title just like every other Wagtail page has a title. We've got our rich text field. This is an intro. This has uh code and centering it because in a prior lesson we actually added additional rich text extension so we added centering text and inline code Next we have our form fields. This is what we're really trying to get to here. So we have a form field here. We can give this a label. We can give it help text. Is this field required? What kind of field type is this? By default? It's not selected, so you can select a single line text, multi-line text, email, number, URL, checkbox, checkboxes, drop-down, multiple select, radio buttons, date, date and time, or a hidden field. Your choices would be, for example, if you had checkboxes, you could have choice one, two, three, and four, and your default value could be, for instance, three.
So this is just a fairly simple way of making something complex relatively easy. It just gives you a regular input field for you to put all of your answers in and then one to match these. And then just one to match whatever answer you want to be the default. Okay, so you can add another one in here. You could put again the same thing. It's orderable in true Wagtail fashion. This is beautifully orderable. You can delete these things. Your thank you text is, you're going to notice that there's a thing down here. I'm going to show you that in just a moment. Your thank you text is basically, again, another rich text field that we're going to show on one particular page. And then we have email settings. And so I was mentioning the whole column thing. We've got six column span here and a six column span here.
So our from address, if I just head on back over to VS Code, where is it? Right here. On line 37 to 40-ish. So we have a field row panel. Field row panel is this whole line here. So it's a field row. And then we have a field panel with a class name of cool six and a field panel with a class name of cool six. And so we see that you have six out of twelve and six out of twelve. So Basically that's all that is. So that's a little crash course on field row panel and simply a subject. Lastly, we have a preview. We can preview the form itself. We can preview the page. That's what everybody's going to see when they land on it. And then there's this landing page. And the landing page is once you submit your form, you're actually going to be taken to another page using a different template, and that page is going to have your thank you text on it.
So it's after the form has been submitted is a different template page. You can preview that one as well. Now, this is not going to work for us. Because we actually don't have any of these fields filled out or anything like that. And I believe actually at the moment we're going to need to run some migrations anyways. So let's go ahead and cancel. Let's do Python 3 managed up high. Make migrations We can see that it created contact page and a form field. Python3 manage. py migrate. And then rerun our server. Yes, leave the page, please. And let's create a contact form. Contact form. So this is not just all of the goodies that comes with Wagtail.
We're actually going to see that there's there's going to be more in here. We're going to see a forms label in here and it's going to be a beautiful feature to work with. So let's write. Hello world. This is intro text. I'll make that slightly bigger. Nothing fancy in there. Let's say we want someone's name. Help text. Enter your full name Field type, single line text, no choices. Let's add another one in here. What is the person's email address? Enter your email address It's going to be an email and let's add a message. So whatever they want to write to you Enter your message. Help text is not required.
I just like filling that in. And where are we? Multi-line text. Uh thank you text thank you for submitting this form something something something Dark Side And we'll be in touch shortly. From address. Who is sending this email? So is it c it's it's coming from your server, so you have to make sure that it's a valid email address. You don't want to fall into any sort of spam traps or anything like that. From email address, I'm just going to use caleb at codingforeverybody. com. And the two addresses are going to be caleblearnwagtail.
com. Contact form submission. Publish the page. Publish the page. Okay. As expected, we did no template work, so we need to make sure that this does actually exist. We'll get there in just a moment. And if we go back to edit. If we were to preview our landing page, we're going to see the same error. However, we're going to see this time that this is not the same page. Contact landing page. or contact page landing rather is different from contact page. So what Wagtail does for its landing page is it says, oh you've got contactpage. html. Okay, well we're just going to append underscore landing to that name. I guess it's not really a full append because it's before the file name extension.
And that's all it's looking for. So fairly straightforward. So let's go ahead and actually create these now. So we need to create our contact page first. Let's go into my site templates and let's create a new directory in here. New file contact contact page dot html and this is going to extend extends base. html. Okay, so we have our contact page in here. We need to be able to put this in somewhere, so let's open up our base. html and find a block that we can put this into. We have a block called content, so let's just slap that in there.
And the only thing we really need is we need a form and we need our intro text. In order to get our intro text actually worked to register as rich text, we need to load a Wagtail Core tag. And that gives us the ability to add the filter. Which looks like pipe rich text. So this is what I'm going to do in here. I'm just gonna put a paragraph self dot Intro text, just intro, rich text, and let's put a title in here, our page title, page. title. Remember for the most part, page and self are interchangeable And when I save this, we're going to see that we're going to see that WakeSelcore tagged is not a registered library.
Silly Caleb. It's tags. It's plural. Because there's multiple tags. So we've got a contact form. Hello world. This is intro text. Pretty straightforward stuff at this point. Nothing we haven't dealt with before. Let's add an actual form now. So Let's add a form. Our action is going to be. . This is an interesting one. Page URL page. And basically that's just itself. Uh the method. Method is important. Method is equal to post. Let's go ahead and make that lowercase method And in here we need two things. We need uh three things actually. We need the CSRF token.
Next we want form, and we're just going to be lazy with this one. So bear with me, this is not going to look great, but it's going to work We're going to use as P. No, I don't like that. We're going to use as UL. And that's going to take the form and that's going to throw all the form fields into an unordered list. The reason I didn't go with as p is because uh if there is a div in there or another paragraph, it's going to basically be nested paragraphs inside of nested paragraphs, which is sort of a no-no. You're not supposed to do that. And lastly, we need an input. We need this to be submit. That's good enough. Alright, so we have our name, whatever our name is, enter your full name, or email address, enter your email address, and a message, enter your message.
And if we look at the source of some of the stuff, inspect element. We can see that it is not styled right now. Our help text has a class called help text, but we're not applying any of our own styling to it. Each one of these is its own line, so that's pretty nice. And then basically you have a label, you've got an input, and then you've got a span. And so you can apply your own styling using CSS to make this nice and pretty We don't really care. We can see that the name is required, the email is required, and also the message is going to be required. So let's go ahead and throw something in here. What is my name? My name is My name is this text is not big enough Caleb Tallin, my email is going to be Caleb at learnwagtail. com and my message is going to be, oh, hello
there. Can you write me back? Please. Submit query and we're gonna see an error. Wait for it. Wait for it. Haha! We're familiar with this one. We've already seen this when we tried to preview our page and it didn't work. So our contact page landing does not exist. Alright, so if you go back to your admin and you give it a refresh, you'll see that there's a section here on the left that says forms. Now this is brand new and this only shows up if there are forms. Now this is really nice. What this is going to do is say you have a page, it's called contact form. You could have another contact page called anything else. And that will show up in here. And this is basically a category, a category listing rather, for understanding where forms are being submitted from.
So we submitted the form once. We have a submission date, a name, an email, and a message. Now if this was in production, it would have emailed me, but we're not in production, so it's not going to email me. What I can do here is I can select multiple emails that have come in. I can delete them, I can download them as a CSV, and I can filter them using a date filter, which is really, really nice. If you wanted to see all your Submissions for the last seven days, that is not a hard thing to do. This all comes with Wagtail. The only thing that's missing at this point is this contact landing page. So let's make that contact landing page. We're going to save this as contact page landing. html and we don't need the form in there.
And we need the Thank you text, which is what I believe we called it. Thank you text, right? Thank you text, it's a rich text field. And let's change the page title because that page title. . No, you know what? Page title's fine. It's going to be the exact same anyways. So uh let's go ahead, come back here I'm gonna hit back a few times just so I can get back to the page that I was on. Preview my landing page. Contact form, thank you for submitting this form, something, something, something, dark side. We'll be in touch shortly. Beautiful. So this is what people are going to see. Now as proof that this is going to work, what I'm going to do is refresh this page and I'm going to fill this in again with Jacob Tavlian. And his
email is example at example dot com blah blah blah blah blah blah blah blah blah blah doesn't really matter submit query and now our form actually works And if we go back into our forms in here, leave page , still says that we have one contact form. This would show too if we had a secondary one. And now we have two submissions in here and it shows me exactly what day these are submitted on, who the name was, and it's going to show you every single field as well, by the way. So if you have more than just name, email, and message, this will go on And on and on and on, and it will extend over towards the right, and it's just a responsive table. It goes all the way to the right.
And you can select all of these if you wanted to. You could download these as a CSV. I just want to save that We could filter these to make sure that it's only from March 13th. And if I wanted to, I could delete these. And voila, those are contact forms in Wagtail. Now I want to give you a heads up right now. Just be careful with these The reason for that is these are unprotected. You want to use some sort of system like a recapture. As of right now, this is a very naked form. Anyone can submit this This is a good time to understand what a recapture is. So if you're interested in that, you can always check out uh Wagtail Recapture, I believe it's called. Alright, so that's all there is for this lesson. We have learned
a great deal of things regarding contact forms. We have learnt that Wagtail comes with a lot of really, really nice features right out of the box. We've learned that we can create a contact form page, but there's also a landing page and that when contact form data is submitted or when the form is submitted, that we can actually go and see when it was submitted, who submitted it. And we can see all the form fields, all that good stuff is in there. Now don't forget, you can always check out this entire code. The entire commit will be available on github. com. I'll leave the link in the description down below If you're interested in learning more about Wagtail, you can always check out the rest of this YouTube playlist or you can go to learnwagtail. com. If you want to learn more about abstract email forms and and all that good stuff, you can always check out the Wagtail Docs at docs.
wagtail. io. And lastly. If you liked this video, you can share, you can subscribe, and you can leave a comment about what you liked. Thanks again for tuning in and learning about Wagtail contact forms, and we'll see you in the next video.
Create a form-field model inheriting from `AbstractFormField` and a contact page inheriting from `AbstractEmailForm`. Add configurable intro and thank-you text, inline form fields, and email settings such as the from address, recipient address, and subject.
Discussed at 2:18Wagtail supports single-line and multi-line text, email, number, URL, checkbox, multiple checkboxes, drop-down, multi-select, radio buttons, date, date/time, and hidden fields. Form fields can also have labels, help text, required status, choices, and a default value.
Discussed at 11:47The form page uses the explicitly configured template, such as `contact/contact_page.html`, while the post-submission landing page uses the same name with `_landing` appended, such as `contact/contact_page_landing.html`. The form template posts back to the page with a CSRF token and renders the form fields, while the landing template displays the thank-you text.
Discussed at 17:28Wagtail adds a Forms section in the admin where submissions are grouped by form and show their submission date and field values. You can select and delete submissions, download them as CSV, and filter them by date.
Discussed at 22:08The basic form is unprotected, so anyone can submit it. The speaker recommends adding a system such as reCAPTCHA before using it in production.
Discussed at 24:25Note: 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