We’re going to subclass a blog post page so we can have multiple types of blog post pages!

There will be 1 model with all the information you need, and then any other class that subclasses this will also have those fields but can modify and add additional properties.

Here is our Parental page we will be subclassing:

class BlogDetailPage(Page):
    """Parental Blog detail page"""

    template = "blog/blog_detail_page.html"

    custom_title = models.CharField(
        max_length=100,
        blank=False,
        null=False,
        help_text='overwrite default title'
    )
    banner_image = models.ForeignKey(
        "wagtailimages.Image",
        blank=False,
        null=True,
        related_name="+",
        on_delete=models.SET_NULL
    )

    content = StreamField(
        [
            ("title_and_text", blocks.TitleAndTextBlock(classname="text_and_title")),
            ("full_rich_text", blocks.RichTextBlock(classname="full_rich_text")),
            ("my_rich_text", blocks.MyRichTextBlock(classname="my_rich_text")),
            ("cards", blocks.CardBlock()),
            ("cta", blocks.CTABlock()),
        ],
        null=True,
        blank=True,
        use_json_field=True
    )

    categories = ParentalManyToManyField("BlogApp.BlogCategory", blank=True)

    content_panels = Page.content_panels + [
        FieldPanel("custom_title"),
        FieldPanel("banner_image"),
        MultiFieldPanel(
            [
                InlinePanel("blog_authors", label="Author", min_num=1, max_num=4)
            ], heading="Author(s)"
        ),
        MultiFieldPanel(
            [
                FieldPanel("categories", widget=forms.CheckboxSelectMultiple)
            ], heading="Categories"
        ),
        FieldPanel("content")
    ]

And our first subclass:

# First subclassed blog post page
class ArticleBlogPage(BlogDetailPage):
    """For article-style blog posts"""

    template = "blog/article_blog_page.html"

    subtitle = models.CharField(max_length=128, blank=True, null=True)

    intro_image = models.ForeignKey(
        "wagtailimages.Image",
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        help_text='Best size is 1400x400'
    )
		
		content_panels = BlogDetailPage.content_panels + [
		        FieldPanel("subtitle"),
		        FieldPanel("intro_image")
		    ]

Now we can create this page in our admin!

If we have a queryset of many BlogDetailPage’s, you won’t be able to check the fields specific to it’s children. For that, we use the .specific modifier.

#wrong
{{post.subtitle}}

#right
{{post.specific.subtitle}}

A problem with .specific is that it uses many more SQL queries than it normally would, this can add up and create some performance issues!