Wagtail CMS: Getting Child Class Properties Using .specific

This video is from Wagtail CMS 2023 .

Wagtail CMS: Getting Child Class Properties Using .specific
0:11:26
Published November 29, 2023
3,124 views

In Wagtail CMS, and just like in Django, you can subclass classes. In this lesson, we're subclassing a Wagtail Page into 2 child pages. But when we query for all the parent classes, we're also given the child classes in the QuerySet, and the data is somewhat inconsistent because child classes can have unique fields that differ from their parents and siblings.

In this video we explore how to use Wagtails .specific() method to get all the child classes in a QuerySet.

Tutorial: https://learnwagtail.com/tutorials/getting-child-page-properties-from-a-subclassed-page/

Git Commit: https://github.com/CodingForEverybody/learn-wagtail/commit/08fbcd67748cec18d9fd8a59fd3c775f6be07aae

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

Summary

Wagtail’s base page queryset returns parent-class instances even when some records are child pages, so fields defined only on child classes—such as an article subtitle—cannot be accessed directly. Calling `.specific` retrieves the concrete child-page instance, allowing templates and Python code to access subclass fields and conditionally display them. The speaker demonstrates this in a blog listing and Django shell, while warning that `.specific` adds database queries and should not be used unnecessarily.

Key takeaways

  • A queryset for a parent Wagtail page type can contain child pages while exposing only the parent model’s fields.
  • `.specific` resolves a page to its concrete child class so subclass-only fields such as `subtitle` are available.
  • Templates can use the resolved object to display optional fields when they exist.
  • Using `.specific` increases database queries, so it should be applied selectively.
  • The same behavior can be inspected and tested in the Django shell with parent and child page objects.

Summarised automatically from the transcript.

Transcript

1,831 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 our last lesson, we created subclasses and we actually ran into an issue where Whenever we were trying to get subclassed information, so that would be data that is particular, or a a Django field rather, that is particular to a subclassed page, and that is not found on a parent page, we weren't able to access it. And a good example of this is if we open our blog models. py and we go down to our listing page, so let's find our listing page here And where is our context? There it is. We did blog detail page dot objects dot

0:45

live dot public So it's just grabbed all of the live public pages that we have available. But the problem is that there are different detail pages now. So if I scroll on down, we have a blog detail page. This is what we used as our parental page or the initial page that we wanted to use as an inheritor. And then we had our article blog page, which inherits the blog detail page, and we also have a video blog page which also inherits blog detail page. And so when we ran blog detail page. objects dot all or dot live and dot public, it got all of these The video blog page has YouTube video ID property on it, but the article blog page does not.

1:31

It simply has a subtitle and an intro image And if we come up here to our blog detail page, there's no video YouTube ID and there's also no intro image or subtitle at all. So how do we deal with this kind of difference? Well, the solution in well at least in Wagtail is really really elegant. It's using dot specific. Now I'm just gonna boot up my server here, so let's do. Python 3 manage. py run server. And in the last video, this is where we left off. We have a blog post with a title, read more link, and an image. Now they all have some sort of banner image, that's why they all show up, but what we wanted to do was we wanted one of these to have a subtitle.

2:19

Now the issue is that not all of these have a subtitle. These are different blog pages. So there are a couple blog detail pages, there's an article blog page and a video blog page. Now this gets a little bit tricky because they don't all have subtitles, so what can we do? If we open up our blog listing page. In the last video we did if post. subtitle show the subtitle. And you would expect that to work because if there is a subtitle, but the problem is that's not how this works. And we'll and we'll enter the Django shell and we will get into this in just a little bit But the solution to this is using post. specific. Specific. And when I go and refresh this page, there's going to be a subtitle right here. So now we have this subtitle, but

3:06

There's one additional thing that you need to know about using . specific is that it is going to try to hit your database more often. So whenever you use . specific, you know your page is going to start adding up in database queries. We can see that in the Django debug toolbar here. where we have SQL queries is equal to 26. If we get rid of the dot specific, which we will actually do, I will demonstrate this. We will go from twenty-six to twenty-four. And if I put that back We go back up to 26. So just be careful. Don't use dot specific too often because it is going to bloat the number of queries on any given page. So that's just a little warning there.

3:52

Now I said we're going to get into the Django shell, so I think we should do that now. And we do Python3 manage. py shell plus dash dash i python Now, if you're just joining us now, the Shell Plus comes from Django extensions and the dash dash iPython comes from iPython, and that just gives us a much nicer feel for our Django shell Now let's go ahead and get all of our blog posts. Blog post is equal to blog detailpage. objects dot all And if I type posts, we see that we have a query set. We have a blog detail page, another blog detail page, another blog detail page, and another blog detail page.

4:37

So we have a blog detail page. It's called first blog post. We've got another one, blog post two, blog detail page. That one's called article blog page and a blog detail page called video blog article. Now this is actually incorrect. If we were to go into the admin right now, we would see that we have two blog detail pages, the first one and the second one, but our article blog page is actually an article blog page, and our Video blog article, which is terribly named. Uh should that one should be a video blog page. And uh let's go take a quick look here. Let's again open up nope Let's open up our blog models. py. So we have an article blog page and we have a video blog page

5:22

And again we have that blog detail page. So we technically have these three pages. One of them is a parent, two of them are children. But in our in our query here, and That is essentially mimicking our query here for the most part. It thinks that all of these blog pages are blog detail pages, because that's what we're asking for. We're saying, hey, Django. Hey Wagtail, go and get all of the blog detail pages and return them back to us, please. Go and get just the blog detail pages. If you've ever run into a mother anywhere, you will know that there's a blog detail page, that's the parent page But whenever

6:08

whenever you request a parent, chances are that parent is going to show up with their child. And that's exactly what happened here. That parent showed up with their child. The article blog page and the video blog page So now what happens if we do a loop here? So let's do four post in posts and let's print post. subtitle. Now we know only one of these pages has the subtitle. And we also know that it's not PISATS, that it's posts It says here that blog detail page object has no attribute subtitle. Now we know that's totally wrong. We know that's totally wrong. And we can do this by testing out article is equal to article.

6:58

blog page dot objects dot let's just grab the first one so we have an article in here The article dot subtitle exists, but if we grab the video page, so let's do video is equal to Video blog page dot objects objects dot first. We'll just grab that first one. Video dot subtitle. This is going to throw me an error. This says object has no attribute, subtitle. And that's that's technically true. And the parent pages also don't have this. So that's technically true. So how how do we deal with this? Well, what we can do is for post in posts. If

7:43

post dot specific dot subtitle print post dot specific noop noop noop noop noop specific. subtitle and let's do an else in here. Else print. Let's just do the regular title. And Let's make sure that we know that this one is definitely the subtitle. So let's make this ugly and say sub in caps. Subtitle This still throws us an error. Now again, the reason for this is because In here when you're doing if post dot specific. subtitle, well, there still is no subtitle on that first

8:29

page. Whatever that page is, we know that it's a blog detail page, has no attribute of that. So what we can do is we can try and accept it. So let's use our video page as an example. So we have video in here. Video. subtitle is going to throw us an error. But what we can do here is try. Let's do this. Print. Video dot specific dot subtitle Except And I'm just gonna throw a generic exception in there. Probably a bad idea to do that, but for demonstration purposes, I don't think that matters. In production, you're definitely going to want to use a proper exception. We're just gonna say error.

9:16

And there it is, it just says error. That is it Now if we did the exact same thing with instead of video we did do do do do do let's go into article article It says welcome to my subtitle. Now that's essentially all that our template is doing here. Our template is saying, oh, if this exists, if there's no error, if there is something here, if it's If it's not null, if it is filled out, if there is some sort of value in here, then simply go and Printed back to the page. The use cases in this would be when you are using any sort of uh parent page. So for instance, we have this blog detail page.

10:02

Do to do to do where are you blog detail page. So whenever you have this blog detail page and you know that there are child pages that may or may not have the exact same data that you're using on, for example, a detail page, or in our case, a blog listing page, you're going to want to use dot specific. And a good analogy for this is think of think of a father and a son, and both their names are Robert. Okay, so you've got Robert and you've got Robert Jr. Someone says, hey Rob, it's dinner time. Well both Roberts are going to respond, okay. But you don't know which one you're actually talking to. So it gets a little bit vague in there. So what you can say is, I will always be talking about the parent

10:49

unless I specifically need to talk about the child. And that's where specific comes in. You are specifically talking to the child. For me, at least that's how I sort of made sense of this, uh, and that's how I made it really memorable. If you have a better analogy, I would like to hear it down below in the comment section. My name is Caleb Tallin. Thank you for joining me today on this lesson on using Wagtails. specific. Don't forget, you can share, you can subscribe, you can comment. If this video was not specific enough for you, then you can always check out the docs at docs. wagtail. io

Questions this talk answers

How do I access fields defined on a Wagtail child page when querying the parent page type?

Use Wagtail’s `.specific` property, such as `post.specific.subtitle`, to resolve each result to its actual child page class and access fields defined there.

Discussed at 1:31

Does using `.specific` add database queries in Wagtail?

Yes. Resolving pages with `.specific` causes additional database queries; in the example, the page went from 24 queries to 26, so it should not be used unnecessarily.

Discussed at 3:06

What does Wagtail `.specific` do?

A query for the parent page type can return child pages as parent-class objects. `.specific` identifies and returns the concrete child-page object, so its subclass fields and methods are available.

Discussed at 5:22

How do I handle a child-page field that does not exist on every Wagtail page?

Resolve the page with `.specific` and guard the field access, for example by checking for a value in the template or using exception handling when some concrete page types do not define that field.

Discussed at 8:29

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