Wagtail get parent page gotchas (tldr; use specific property)
I was implementing a tag page, a page for listing blog entries for a particular tag using RoutablePageMixin
.
class BlogIndexPage(RoutablePageMixin, Page):
intro = models.CharField(max_length=250)
content_panels = Page.content_panels + [
FieldPanel('intro')
]
@path("t/<str:tag>/", name="blog_tag")
def blog_tag(self, request, tag):
blogpages = BlogPage.objects.child_of(self).live()
blogpages = blogpages.filter(tags__name=tag)
return self.render(
request,
context_overrides={
"blogpages": blogpages,
},
template="kai_site/blog_tag_index_page.html"
)
And then in the blog page views templates we will list all tags the page is associated with:-
...
{% if page.tags.all.count %}
<div class="tags">
<h3>Tags</h3>
{% for tag in page.tags.all %}
<a href="{% routablepageurl blog_index url_name='blog_tag' tag=tag %}"><button type="button">{{ tag }}</button></a>
{% endfor %}
</div>
{% endif %}
</article>
And blog_index
is defined as:-
class BlogPage(Page):
date = models.DateField("Post date")
intro = models.CharField(max_length=250)
tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
...
def get_context(self, request):
context = super().get_context(request)
context["blog_index"] = self.get_parent().specific
return context
However I got the following error:-
File "/workspace/.pyenv_mirror/poetry/virtualenvs/kai-site-gqAITAgE-py3.10/lib/python3.10/site-packages/django/template/library.py", line 237, in render
output = self.func(*resolved_args, **resolved_kwargs)
File "/workspace/.pyenv_mirror/poetry/virtualenvs/kai-site-gqAITAgE-py3.10/lib/python3.10/site-packages/wagtail/contrib/routable_page/templatetags/wagtailroutablepage_tags.py", line 25, in routablepageurl
routed_url = page.reverse_subpage(url_name, args=args, kwargs=kwargs)
AttributeError: 'Page' object has no attribute 'reverse_subpage'
Checking what the page
instance is shows that:-
>>> type(page)
<class 'wagtail.models.Page'>
>>> page.__class__
<class 'wagtail.models.Page'>
So I'm getting the base class, not my subclass BlogIndexPage
. Took some time debugging with me getting off-tangent thinking that the blog page was created with the wrong parent. But peeking into the database shows everything is in order.
It only after looking into wagtail source code for the Page models I found this:-
def get_specific(self, deferred=False, copy_attrs=None, copy_attrs_exclude=None):
"""
Return this page in its most specific subclassed form.
By default, a database query is made to fetch all field values for the
specific object. If you only require access to custom methods or other
non-field attributes on the specific object, you can use
``deferred=True`` to avoid this query. However, any attempts to access
specific field values from the returned object will trigger additional
database queries.
So that's it!
>>> type(page)
<class 'wagtail.models.Page'>
>>> page.__class__
<class 'wagtail.models.Page'>
>>> page.specific
<BlogIndexPage: Blog>
Page.get_parent()
only return the instance with the base class as its type. We need to use the .specific
property to get our sub-class as its type.