Reputation: 436
I have two pages:
ArticlePage and TimelinePage.
TimelinePage has an inline panel which is related to the model TimelinePageEntry.
TimelinePage displays all of it's child TimelinePageEntry(s) just fine. However when I try to get all the TimelinePageEntry(s) from ArticlePage, it fails with an exception.
ArticlePage fails with an exception when I try to do:
context['timeline_entries'] = timeline_page.timeline_entries.all()
Here's the source code for the three classes in models.py:
import datetime
from django.db import models
from modelcluster.fields import ParentalKey
from wagtail.admin.edit_handlers import FieldPanel, InlinePanel
from wagtail.core.fields import RichTextField
from wagtail.core.models import Orderable
from wagtail.core.models import Page
class ArticlePage(Page):
date = models.DateField("Creation date", default=datetime.date.today)
hero_biography = RichTextField(blank=True)
content_panels = Page.content_panels + [
FieldPanel('date'),
FieldPanel('hero_biography'),
]
subpage_types = [
'articles.TimelinePage'
]
def get_context(self, request, *args, **kwargs):
context = super().get_context(request)
timeline_pages = self.get_children().type(TimelinePage)
timeline_page = timeline_pages[0]
# prints "Page"
print(type(timeline_page).__name__)
# prints the title of the timeline page, so I know I'm getting a query result
print(timeline_page.title)
context['timeline_page'] = timeline_page
# Throws an exception
context['timeline_entries'] = timeline_page.timeline_entries.all()
return context
class TimelinePage(Page):
max_count_per_parent = 1
subpage_types = []
content_panels = Page.content_panels + [
InlinePanel('timeline_entries', label="Timeline Entries")
]
parent_page_type = [
'articles.ArticlePage'
]
def get_context(self, request, *args, **kwargs):
# Prints "TimelinePage"
print(type(self).__name__)
# Prints a list of timeline entries
print(repr(self.timeline_entries.all()))
return super().get_context(request)
class TimelinePageEntry(Orderable):
page = ParentalKey(TimelinePage, on_delete=models.CASCADE, related_name='timeline_entries')
start_date = models.DateField()
end_date = models.DateField(null=True, blank=True)
line = RichTextField(max_length=90)
panels = [
FieldPanel('start_date'),
FieldPanel('end_date'),
FieldPanel('line'),
]
class Meta(Orderable.Meta):
ordering = ['-start_date']
It seems like what I am trying to do should be really simple. TimelinePage is a child of ArticlePage. I am able to access other attributes of TimelinePage (the title) from ArticlePage, but I'm stumped on how to get the TimelinePageEntries.
The exception that I get is:
context['timeline_entries'] = timeline_page.timeline_entries.all()
AttributeError: 'Page' object has no attribute 'timeline_entries'
Upvotes: 0
Views: 416
Reputation: 25292
This is due to the distinction between Page
instances (which just contain the data common to all page types, such as title) and instances of your specific TimelinePage
subclass (which provides access to all the methods, fields and relations defined on that class).
self.get_children()
returns a queryset of type Page
; this is necessary because the query doesn't know in advance what page types will be included in the results. .type(TimelinePage)
filters this to just pages of the given type, but this only acts as a filter on the existing queryset - the results will still remain as Page
objects, which are missing the timeline_entries
relation.
If you rewrite the line
timeline_pages = self.get_children().type(TimelinePage)
into
timeline_pages = TimelinePage.objects.child_of(self)
then this will ensure that the results are returned as TimelinePage instances, and the timeline_entries
relation will then work as intended.
Upvotes: 3