pjgb
pjgb

Reputation: 15

Wagtail CMS: Finding all pages that have a relation to the current page

I'm trying to understand how to get links to all pages in my site that have the currently viewed page listed as a related page via the modelcluster ParentalKey.

The basic setup is as follows:

# RelatedLink inherits from LinkFields, 
# both directly copied from the wagtaildemo project

class ParentPageRelatedLink(Orderable, RelatedLink):
    page = ParentalKey('ParentPage', related_name='related_links')


class ParentPage(Page):
    parent_page_types = ['ParentPage']
    subpage_types = ['ParentPage', 'ChildPage']

    def child_pages(self):
        children = ChildPage.objects.live().descendant_of(self)
        return children

ParentPage.content_panels = [
    FieldPanel('title', classname="full title"),
    FieldPanel('body', classname="full"),
    InlinePanel(ParentPage, 'related_links', label="Related links"),
]


class ChildPage(Page):
    parent_page_types = ['ParentPage']
    parent_page_types = ['ChildPage']

    def parent_index(self):
        return self.get_ancestors().type(ParentPage).last()

ChildPage.content_panels = [
    FieldPanel('title', classname="full title"),
    FieldPanel('body', classname="full"),
    InlinePanel(ChildPage, 'related_links', label="Related links"),
]

If understand things correctly, to get each ParentPage that has the current ChildPage in its related_links, I'd have to go through every page listed in ChildPage.parent_page_types, test if the current ChildPage is in the ParentPage.related_links, and then output whatever I need from each of those ParentPages.

Seems like it would be a lot of queries to the db if there are many instances of the page types listed in parent_page_types.

Is there a better way?

For example, does modelcluster enable any sort of backreferencing (like what Flask-SQLAlchemy provides when using db.relashionship(backref="something")) through the ParentalKey that is created in ParentPageRelatedLink? It doesn't look like it from inspecting the database tables.

Edit

Ok so it seems like the related_name from the LinkFields might be a way to do this, but since I can't set it to something like "related_from" since LinkFields is inherited by many different ParentPage-like classes, it seems I have to have individual LinkField classes with their own unique ForeignKey(related_name="something") definitions for each ParentPage... Or do as instructed in the django docs. But then I might be better of with my initial thought of a loop?

class LinkFields(models.Model):
    link_external = models.URLField("External link", blank=True)
    link_page = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        related_name='+'
    )
    link_document = models.ForeignKey(
        'wagtaildocs.Document',
        null=True,
        blank=True,
        related_name='+'
    )

    @property
    def link(self):
        if self.link_page:
            return self.link_page.url
        elif self.link_document:
            return self.link_document.url
        else:
            return self.link_external

    panels = [
        FieldPanel('link_external'),
        PageChooserPanel('link_page'),
        DocumentChooserPanel('link_document'),
    ]

    class Meta:
        abstract = True

Upvotes: 1

Views: 1880

Answers (2)

Karl Hobley
Karl Hobley

Reputation: 41

Try this:

parent_pages = ParentPage.objects.filter(related_links__linked_page=child_page)

"related_links" is taken from the related_name attribute of the ParentalKey on the model that you want to query. "linked_page" is a field on the ParentPageRelatedLink model that you want to filter on.

https://docs.djangoproject.com/en/1.8/topics/db/queries/#lookups-that-span-relationships

parent_page_types is unrelated to querying pages. It's used to add a constraint to what page types a particular page type can be created under (so you can say a blog entry can only ever be created in a blog for example). It's unrelated to how pages are queried

Upvotes: 2

rnevius
rnevius

Reputation: 27102

You should be able to define a @property within the ParentPage class. Something like:

class ParentPage(Page):
    ...
    @property
    def related_pages(self):
        pages = ParentPageRelatedLink.objects.filter(page_id=self.id)
        return pages

Upvotes: 1

Related Questions