What the Wagtail docs don't tell you

This video features Lacey Henschel at Wagtail Space US 2018 in Philadelphia, Pennsylvania, USA.

0:23:47
Published June 21, 2018
615 views

From Lacey: "Wagtail is a great Django CMS, but getting started with it is a little intimidating. Wagtail has a ton of useful features that their docs don’t go into, so this talk is here to help! You’ll get to know the Page class, add redirects, make your Page relationships healthier, and handle users easily."

Summary

Lacey Henschel praises Wagtail’s upgrading, getting-started, and editor documentation, then identifies areas where developers need more concrete guidance. She explains the Page model and its fields, parent–child page type restrictions, programmatic page creation and revision publishing, redirects for migrated URLs, and the pattern for exposing selected users through a page chooser. Her central argument is that these workflows are possible but difficult to discover, so the documentation should include clearer explanations, code examples, migration recipes, and complete solutions to the exercises it proposes.

Key takeaways

  • The Wagtail Page model contains important inherited fields and methods, including first_published_at and save_revision, that are especially relevant during content migrations.
  • Parent_page_types and subpage_types work together to constrain which page types can contain or create one another, and both sides must be configured for a two-way restriction.
  • Programmatic page creation requires setting model fields, attaching the page to its parent, saving a revision, and publishing that revision rather than simply calling save().
  • Bulk migrations may need programmatic permanent redirects to preserve old URLs, provided the old paths resolve to 404 responses.
  • To select a specific group of users in the admin, a model can relate users to a TeamMember page and use a PageChooserPanel rather than foreign-keying directly to the user model.
  • Wagtail documentation would be more useful with discoverable examples and guidance for migrations from Django, Drupal, WordPress, and other systems.

Summarised automatically from the transcript.

Transcript

4,855 words · auto-generated Show

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

0:00

Speaker 1: Yeah, thanks.

0:01

Speaker 2: Awesome.

0:01

Speaker 1: All right. Hi everyone. Good morning. Tom mentioned in in his talk about the future of Wagtail talking a little bit about the future of the documentation and He mentioned that Danielle has some ideas about the organization of the documentation. I have some very specific ideas about uh concrete places where maybe the documentation could be a little bit stronger, so we're going to talk about that. Um as Tim mentioned, I do work for Repsys. We're a Django consultancy based out of Kansas. We do a lot of work in Django Rest Framework, React, Docker, and of course Wagtail. So if you want to talk about things that we we could do for you definitely come talk with me. We've worked with Wharton before. So yeah. Also I'm one of the organizers for DjangoCon US and um Wagtail is obviously built on Django. So our tickets are on sale now. The conference will be in San Diego in October.

0:46

Speaker 1: Feel free to grab those early bird tickets before they are gone. And before I get started telling you kind of where the Wagtail docs could be a little bit better, I do want to compliment Wagtail. Your docs as they are are really, really good. Particularly the upgrading documentation. I recently had to take a website from like 1. 6 to 2. 0 and that was way less painful than I thought that it would be because that upgrade documentation is very, very detailed. The tools that are built in were very helpful Also, the Getting Started Guide is very helpful too. I only started using Wagtail about eight months ago and going through that first tutorial was was very, very nice. And then the documentation that Wagtail has for end users who are going to be using that admin UI to manage the websites is also really, really nice. It seems very thorough to me

1:33

Speaker 1: But I am gonna have some ideas in here about things that we could sprint on since there are sprints happening today. Um so if I can kind of talk with some of you after this to see if you agree with me that these are places Where things could be improved. I won't actually be here tomorrow, so I won't be sprinting, but that doesn't mean that I can't submit my own pull request. I also want you to know that I am a big liar. Some of the stuff that I'm gonna tell you is not documented, actually is documented. It's just not documented as fully as I would like it to be. Or the documentation is kind of hard to find. Maybe it's buried in references. But I do want you to know that sometimes I'll say that this isn't documented and that it's completely a lie. As I said too, I've only been working in Wagtail for about eight months, so I am not an expert at all. My first project in Wagtail was migrating a website, a blog, particularly from Django, from using regular Django models

2:21

Speaker 1: over to Wagtail and I didn't find very many resources about how to do that and so a lot of what I came up with was just kind of stuff that I threw together. So some of the code samples I might show you, there might be much better ways of doing what I did. I would love it if you would come and chat with me about that so that I can get better at doing that kind of migration because as Wagtail gets more Widely adopted, probably more and more of those content migrations will become part of our lives. So the first thing that I want to talk about is that page model, like everything Wagtail inherits from the page model. So that's a pretty important thing to understand And again, it is documented. There is the model field reference that's linked to in the Wagtail docs. I've also linked to the code in the repo because when I was doing my project, I wound up just digging through the code quite a bit.

3:06

Speaker 1: So again, there is documentation on this, but the model field reference is buried in resources. It is linked to from the Getting Started guide, but it just says inherit from the page model, here's the model field reference, and go And the reason that you might want a little bit more detail than that are a couple of things. There's a lot of really great stuff in the page class. There are a lot of attributes that you don't want to duplicate whenever you're writing your own model. It also has some methods that might be helpful. And again, those are documented, but that's that's a little bit varied. And then whenever you're dealing with pages programmatically, like in a content migration you're going to want to be pretty familiar with that page model so that you know what you need to do. So this is a heavily redacted and truncated version of the page model. You'll see it's missing all kinds of

3:52

Speaker 1: of stuff that that we need. But I just wanted it to be easy to read. And just in case you're you're not familiar with the very, very detailed um aspects of the page model, I just wanted to go through a couple of things. So the title slug, SEO title, show in menus, and search description fields, they map to that promote tab. And that might be something that is kind of useful to know in the documentation for people who are just starting out The go live at and inspire or expire at fields map to that settings tab that's in the admin And then this first published at, this one's a little bit tricky. It's pretty important for a content migration. It's how you backdate your posts. The first published at does not appear in the admin. Of course you could edit the admin so that it did, but whenever you're doing a content migration, if you have a blog or some other content where the date matters

4:40

Speaker 1: Wagtail doesn't let you natively in the admin say, hey, I want this to have been published in 2015, not today. So figuring out how to do that is a little bit tricky. Also, there's the save revision method, which you don't really need to care about until you're doing a content migration. Wagtail gets a little bit upset with you if you try to save a page without having gone through any sort of revision process. So we won't go into details about this, but it is there. When you get into the method, the method itself is reasonably self-explanatory. But there's you know there's a doc string that's pretty helpful. Tim was also very helpful in letting me know that this method existed and why I needed to use it. So thank you, Tim. So my first sprint idea, and we'll keep a running tab as we go here, is maybe find some ways to expand on the page model and the usage guide, maybe give some examples, highlight some of those fields that newcomers might really need to know about that they wouldn't want to do

5:31

Speaker 1: to make things a little bit easier. The next thing I want to talk about is that parent-child page type relationship And this is another thing that is documented. It's um if you just follow this link, it'll go straight to that section in the documentation. But this is what you have. This is basically all that's currently in the documentation. About this relationship. And it's it's correct, it's there, but it doesn't include a code example. It's not super clear, at least it wasn't to me when I was first coming into Wagtail and trying to learn all of this I wasn't super clear on how this relationship worked. And you might be thinking like, well, it's right there. If you just read closely, you'll figure it out. But I'm a big examples person. I really wanted a code example. I really wanted someone to break this down for me. And this is a pretty crucial relationship for defining how your different models work together, for defining how you create different pages in the admin.

6:24

Speaker 1: So understanding this relationship is really important for making sure that your website works the way that you intend it to. So I've set up some fake models here. We have a let's say we have a blog and we have you know posts for our blog. So we have a post page model and we have a post-index page model, and both of those are inheriting from the Wagtail page model itself. And they're defining some things here, right? So we've got parent page types defined in both models, and then we have subpage types defined in just the top index model. So parent page types, if you're not familiar with this, if you're a little bit of a beginner, that defines what pages can create this kind of page. So who can your parents be? And then subpage types, that's the reverse. What pages can this particular page create itself?

7:10

Speaker 1: So who can this page's children be? So in the post index page, whenever we set its parent page type to the home page, and that if you're not familiar with this, that between the quotes, that's just a path to the model that you're referencing. This says that only the home page is allowed to create a post index page. That's the only page that's allowed to create this particular kind of page. And then the post index page has a subpage type of post. postpage, which is just that model that's there at the bottom that's grayed out right now. So that means that the post index page can only have post page children. That's the only kind of child that it can make. In the post page, its parent is the post-index page. So the index page can only create

7:56

Speaker 1: post page children, and the post page can only have post-index page parents. Now these two together define this relationship as two-way. If you intend for one specific kind of index page to only be able to create one specific page kind of page, you have to tell Wagtail that in both places. Otherwise you can say something like post index pages can only create post pages, but post pages can be created by anybody. And that might not be what you intend. So to sum this up, if you want to limit a page to have just one type of child and you want to limit a page to have just one parent, then you have to tell them in both places. And again, this is a pretty important relationship for making sure that your your website works the way that you intend, that the data on your back

8:43

Speaker 1: end um is is structured the way that it should be. So this relationship is pretty important. So that's another sprint idea. Maybe there are ways that we can ex um expand on that section in the document documentation, maybe we could include a code example and kind of break this down a little bit more so that newcomers understand that relationship a little bit more easily. So since I'm talking about content migration here, I want to talk about saving pages programmatically. And the reason that you might care about this is of course a content migration. But also like backdating a page, since that's not very easy to do in the admin unless you edit the admin, if for some reason you needed to backdate a page, then you could do that programmatically And these are I guess kind of the same thing. Making making changes to pages outside of the UI.

9:29

Speaker 1: So not just backdating, but if for some reason you needed to dig into your console and mess with a particular page, being familiar with how to do that is is important to do And in the code snippets that I'm going to show you, we're making some assumptions here. First is that we're using those same models that we just had. post index page and the post page. And we're also assuming that all data validation has happened somewhere else. All of our data is valid and we don't need to worry about it. So this is the section where if there is a better way to do this, I would really love to know that and please come talk to me. This is what I put together and it does work. But But possibly there's a better way. But this is the basic code that you will need to do that process of saving a page programmatically. And I'm going to just go through this not quite line by line, but kind of section by section

10:16

Speaker 1: So first you want to import your post page model and then instantiate a new instance of that model. Then you want to save all of your fields. In this code, we're assuming that we've passed in In a dictionary called fields, and so each field that we need to save is a key in that dictionary. So we're saving the title, the latest revision, the first published at, and then whatever other fields that we have. And these are going to be the fields on the page model itself and also the fields on your model that's inheriting from the page model. So you're kind of accounting for both fields here And that first published app, that's where you get to put the original date that you want to save. So if you're saving some sort of object that was created in 2015 and you want to preserve the 2015-ness

11:02

Speaker 1: That's where you you do that. Now we have to create that that index-to-page relationship. And so you do that by going and retrieving the specific index page that you You want. You might have several index pages. In this one, we're getting this specific index page that's called posts, and we're just adding our post instance as a child to that post index. Then you have to go through this revision process. If you try to just do like post dot save, you're all done. Wagtail gives you an error, and I can't remember what that error is, but it was frustrating, and I I messaged Tim And I asked him, what is going on here? And he very kindly walked me through this exact process. So you you have to go through the the save revision method of your specific post.

11:48

Speaker 1: You can submit this for moderation. If you're saving something programmatically, you're probably not going to want to go through a moderation process for that. So we're not going to go through moderation right now. And then this creates a revision object, which is separate from your post object. So you save your post, and then if you want to publish your post, then you publish your specific revision. So your post and your revision are kind of distinct things, and what you're publishing to appear live on your site technically is your revision of this post. As I've said, it is possible that there is a better way to do this. If there is, please come and talk to me so that I can learn what that way is. But this was kind of an idea that I had. I don't know if it's a good idea to have a method on the page class to add a new page instance programmatically. It might be a little complicated since there's so much inheritance that happens

12:36

Speaker 1: From that model, but maybe some creative thinking can kind of find a way to make that happen. But at the very least, I think that more documentation on how to save a page programmatically, especially since you have that save revision process that Wagtail really wants to wants you to have would be very helpful. When I was doing this content migration, I did a lot of Googling, just kind of assuming like I'm not the first person to walk this path. I'm I'm never the first person to do something. I'm not very groundbreaking. And so it's a huge relief to me that I'm never the first person doing something. I could not find any blogs or videos or anything about this process. So this is kind of what what I you know came up with with with Tim's help. So someone, and it could be me, I should be the one, but someone writing this down would be very helpful. When you're doing a big content migration, you also probably don't want to lose your URLs, so doing a redirect programmatically might be part of your process as well.

13:26

Speaker 1: And I will go ahead and say that there are different ways that you could handle your old URLs. You could just force Wagtail to use that old URL. Um in the case that I was was dealing with this in, the way that our URLs were going to be structured was going to change. So we didn't want to have old posts that had a URL that looked One way, and then new posts that had URLs that looked a different way. But we also didn't want to break all of our links, so we added these programmatic redirects to so that our users wouldn't get 404s for things that they had bookmarked or saved in other places If you need to add a lot of redirects, you know you can add a redirect in the admin. I don't know if if you've done this before, but you can go to the left-hand side of the Wagtail admin. Click the redirects button and then add one. And you basically put in the old URL path, not the domain, but everything after the.

14:13

Speaker 1: com basically. And then you can redirect to a specific URL like Google or whatever, or you can redirect to a particular page in your site. And for this particular code snippet, we're assuming that you have not subclassed the redirect model, that you're just using Wagtail's redirect model. We're also assuming that you only have one site. If you do have more than one site, your process is a little bit different. This code assumes that your use case is pretty simple. This assumes that your redirects are permanent. Redirects, you might not know, only work if your old URL 404s. If your old URL does not 404, nothing will happen. And then this code assumes that we're redirecting to a page and not to a specific URL. So this the code that you need for

14:58

Speaker 1: this is actually pretty straightforward and I think would make a pretty good method on the redirect class What you you need to do is import the redirect model from Wagtail and instantiate a new instance of it. You want to set the old path. I went ahead and stripped out the trailing flash just because That's who I am. But the redirect clean method does include some some URL cleanup for you. So I don't think that this is really necessary. I'm just a belt and suspenders kind of person You set the permanency of your redirect, ours are permanent. You set where your redirect is going. So in this particular function, we're taking in the old URL and the post that we want to redirect to. So we're just saying we want this old URL to go to this particular post, this particular page on our website.

15:43

Speaker 1: Then you save your redirect, and if you want to see your redirect, you can return it. Now since this code was was pretty simple to write and figure out, it's not currently a method on the redirect um model, but I think that it could be. I think that this would be a a pretty great project for a beginner. So if you're looking for something to sprint on and if Tom and the powers that be think this is this is a good idea then I think that this would be something that someone could jump in and add There we go. And then the last thing is using the user model. And this is another thing that there is documentation on. There is a section um in the Wagtail docs about the user model, but I'm gonna go into a little bit more detail because there was one use case that was a little bit confusing. So you might care about how to use Django's user model in Wagtail if for example you have a company website and you have like a team.

16:33

Speaker 1: You know, you want to display your team members, your your programmers, or whoever on a web page. And then maybe you also have a blog where your team members are able to write blog posts and you want to list the particular authors, then but you might have other users on your site, right? Like NHS is gonna have like, you know, doctors and patients or whatever, but they're also gonna have the marketing team. That is going to be maybe writing the blog post, but the patients aren't really writing blog posts themselves, right? So you might want the authors of your blog posts to be pulled from a particular section of of your users. And so that can get a little bit complicated. And you want to be able to access all of that pretty easily in the Wagtail admin. So whenever you go to the custom user model documentation right now, it jumps right into forms. So this is helpful, you know, people might need this, but it kind of shows you only one thing about

17:20

Speaker 1: using a custom user model um and it wasn't really what I needed at the time. So for the case I talked about where you have a blog, you have a lot of users, but you only want blog authors to come from a particular set of your users the people that you want to blog, then you what what I did was I I I basically created a um a a team member page model that inherits from the the Wagtail page class. And then I just set a foreign key to the user. If you're not familiar with get user model, it's pretty handy. If you're using your own custom user model, then get user model from Django will retrieve whatever user model that you're using. If you're using the regular one, it will retrieve that too. And then you just set that foreign key. So you would think that if on your post page, if you had an author, that you might just foreign key directly to that team member page

18:10

Speaker 1: You might think it would look something like this. Maybe your author foreign keys directly to user, maybe foreign keys to this team member page. But you would be wrong. If you do that, it doesn't appear in the admin. If you want it to appear in the admin so that it's selectable, you have to go through a slightly different process. That looks a little bit more like this. And again, it's possible that there's a slightly different way that you could set this up. This is what worked, and this is this is code that I did not write that I I found somewhere. It does work. possible that there are places that it can be cleaned up. But your author foreign keys to WagtailCore dot page, which feels very scary. Your foreign key is going to what feels like some random Wagtail page. How is this going to work? Oops. Oh there we go.

18:55

Speaker 1: Yeah. Um the way the reason that it works is because you use this thing from the Wagtail um admin called Pay Chooser panel. So you set your foreign key and then you go into content panels and you add this page chooser panel that is selecting from your team member page. So if you look at page chooser panel, you have the author, which is referring to the author field on your model, and then you have that team, team member page, which is saying, hey, select authors from this section of people, from this particular model. model. So yeah, maybe adding some more on user model docs, maybe some use cases. There's one section in the getting started guide that mentions something about adding like a profile model, and then it says, but we'll leave that as an exercise to the reader and I don't like exercises that don't have solutions.

19:42

Speaker 1: So maybe like create a solution and link to it or something. I totally get having um you know extensions or something that you want people to do, but providing them with the answer if they get confused is useful as well. So that's all I have for today. I really appreciate everything that Wagtail has done. I really do appreciate the existing documentation, and I do fully intend to implement some of the suggestions that I've had here today myself. But I want to welcome anyone who has questions or has suggestions to come and chat with me about that so that I can improve the way that I do Wagtail and so that together we can improve the documentation. That's me. I actually don't know how I'm doing on time. I can take questions if um

20:29

Speaker 1: if Tim wants me to take questions at it.

20:31

Speaker 2: We've had plenty of time at the end of the day, so for a few minutes behind that's okay if anyone has questions.

20:36

Speaker 1: Any questions? Um

20:42

Speaker 3: Migrating Drupal to Wagtail?

20:44

Speaker 1: We've never done that, no. And I I think that that would be particularly useful because from I've never used Drupal myself, but from what I understand, a lot of people move from Drupal to Wagtail. Um and whenever I was looking for information on migrations, I was kind of looking for that. Like I was just looking for migrating from anything because I figured that the process for saying saving the page would be the same like once you got your data in. But no, I I haven't done an Resis, as far as I know, hasn't done anything with Drupal specifically. And migrating that data. But that would be, I think that documentation on those different kinds of use cases, like migrating from regular Django models to Wagtail, migrating from Drupal or from WordPress to Wagtail, more documentation on how you do that I think would be helpful and would probably help increase adoption.

21:29

Speaker 3: I would say not a question, but one of the things that we run into is with data migrations from like when we make significant changes to our page models. Yeah. It's this same documentation would be massively useful right now.

21:41

Speaker 1: Yeah, yeah. And there's formatting concerns too, right? Like the the data that I was migrating was some of it was in markdown, some of it was in restructured text. And the restructured text, that was really fun. I had to convert it like to HTML and then back to Markdown. And there was a little bit of of kind of editing that I had to do. But getting, especially like for a blog, for example, like getting your data in a format that like Streamfield, for example, is going to understand and format well. is um is pretty important. And so kind of something that that gave people a heads up about what those pitfalls would be would be helpful. Yeah.

22:15

Speaker 4: So I'm in the middle of a transition from a sort of home grown mezzanine based multi-site thing and we want in our Wagtail thing to integrate search across our various sites.

22:28

Speaker 1: Oh

22:28

Speaker 4: so here's The built-in search will easily do the internal pages, but we also want search terms like you know Hercul Maria to show up on our show a link to our other settings. Yeah. I'm in discussion with the Twitchbox folks about this, so if I put my foot in my mouth, please uh forgive me now. But one suggestion is sort of an RSS feed uh that feeds into pages in Wagtail that we never display to the user. But that, and here's the question about redirects. Can I have a redirect of something like Wagtail Invisible Page thing back to the original page in the X

23:14

Speaker 1: I don't know. I would my gut says yes, because if you're if you're not showing a page to a user, then if you tried to go to the URL that exists For that page, you would get a 404 and you can redirect a 404. So I would think that you could. Tom is nodding. So yeah. So yeah, I would think that that's possible. Yeah Any other questions?

Questions this talk answers

What Wagtail Page model fields and methods are important for content migrations?

The Page model provides fields such as SEO metadata, menu visibility, scheduling dates, and first_published_at, which is especially useful for preserving original publication dates. Content migrations also need methods such as save_revision rather than simply calling save().

Discussed at 3:52

How do Wagtail parent_page_types and subpage_types work?

parent_page_types controls which page types may create a page, while subpage_types controls which child types it may create. To enforce a strictly two-way relationship, such as an index page having only post children and posts having only that index as a parent, configure both sides.

Discussed at 6:10

How do you save and publish a Wagtail page programmatically?

Instantiate the page model, set both its own fields and the inherited Page fields—including first_published_at—then add it beneath the appropriate parent. Save it through save_revision, and publish the resulting revision if it should go live.

Discussed at 10:16

How do you create Wagtail redirects programmatically?

Create a Wagtail Redirect with the old path, mark it as permanent if appropriate, set either its destination page or URL, and save it. Redirects only take effect when the old path would otherwise return a 404; the example assumes the built-in Redirect model and a single site.

Discussed at 14:58

How can a Wagtail blog choose authors from a specific group of users in the admin?

Create a team-member Page model linked to Django’s user model, then have the post’s author field reference Page and use a PageChooserPanel restricted to team-member pages. This makes only that selected group appear as author choices in the Wagtail admin.

Discussed at 17:20

What should you watch out for when migrating content into Wagtail?

Beyond creating pages and preserving dates and URLs, migrated content may need conversion between formats such as Markdown, reStructuredText, and HTML, and it must be shaped to work well with fields such as StreamField. The speaker recommends documenting these formatting pitfalls and other migration-specific use cases.

Discussed at 21:41

Presenters

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 by Lacey Henschel

More videos from Wagtail Space US