Rohit.M
Rohit.M

Reputation: 124

Filtering on PageQuerySet (which is retuned by specific()) is returning FieldError in Wagtail

I am using subclassed model of Wagtail Page. In below code you can see that PhoenixPage is base page which subclasses Wagtail Page model.

PhoenixArticlePage & PhoenixMealPrepPage subclasses PhoenixPage

PhoenixArticleIndexPage subclasses PhoenixBaseIndexPage which in turn subclasses PhoenixPage

Idea is to use PhoenixArticleIndexPage for all other article pages.

Problem is even after using the specific() method on queryset i am unable to use filter or any other operation on the queryset. i tried using order_by() as well as filter()

Can someone share some insights here ? what might be wrong ?

Here is a model example:

class PhoenixPage(Page):
    """
    General use page with caching, templating, and SEO functionality.
    All pages should inherit from this.
    """

    class Meta:
        verbose_name = _("Phoenix Page")

    # Do not allow this page type to be created in wagtail admin
    is_creatable = False
    tags = ClusterTaggableManager(
        through=PhoenixBaseTag,
        verbose_name="Tags",
        blank=True,
        related_name="phoenixpage_tags",
    )

class PhoenixBaseIndexPage(PaginatedListPageMixin, PhoenixPage):
    class meta:
        verbose_name = "Phoenix Base Index Page"
        app_label = "v1"

    index_show_subpages_default = True
    is_creatable = False

class PhoenixArticleIndexPage(PhoenixBaseIndexPage):
    class Meta:
        verbose_name = "Phoenix Article Index Page"
        app_label = "v1"

class PhoenixArticlePage(PhoenixPage):
    class Meta:
        verbose_name = "Phoenix Article Page"
        app_label = "v1"

    subpage_types = []
    parent_page_types = ["v1.PhoenixArticleIndexPage"]

class PhoenixMealPrepPage(PhoenixPage):
    class Meta:
        verbose_name = "Phoenix Meal Prep Page"
        app_label = "v1"
    subpage_types = []
    parent_page_types = ["v1.PhoenixArticleIndexPage"]

Here are shell queries i tried.

Index page

In [4]: a  = PhoenixArticleIndexPage.objects.all()[0]
In [5]: a
Out[5]: <PhoenixArticleIndexPage: articles>

As expected, get_children returning all instances of Wagtail Page.

In [6]: a.get_children()
Out[6]: <PageQuerySet [<Page: article title>, <Page: article title2>, <Page: Our 30-Day Reset Recipes Are So Easy AND Delicious>]>

Getting specific children from the Index page.

In [7]: a.get_children().specific()
Out[7]: <PageQuerySet [<PhoenixArticlePage: article title>, <PhoenixArticlePage: article title2>, <PhoenixMealPrepPage: Our 30-Day Reset Recipes Are So Easy AND Delicious>]>

Get Tag and try to filter the queryset

In [8]: q = a.get_children().specific()

In [12]: m = PhoenixTag.objects.get(slug='meal')
In [16]: k={"tags":m}
In [19]: q.filter(**k)

***FieldError: Cannot resolve keyword 'tags' into field. Choices are ...***

But if i go to particular entry in queryset then i can see tags field on it.

In [15]: q[2]
Out[15]: <PhoenixMealPrepPage: Our 30-Day Reset Recipes Are So Easy AND Delicious>

In [16]: q[2].tags
Out[16]: <modelcluster.contrib.taggit._ClusterTaggableManager at 0x1060832b0>

Could be different question all together but for reference adding it here.

Found the corner case of using difference() and specific() method on a queryset.

In [87]: q = PhoenixPage.objects.child_of(a).live()
In [89]: f = q.filter(featured=True)[:3]
In [91]: l = q.difference(f)
In [93]: l.order_by(a.index_order_by).specific() . <-- does not work
DatabaseError: ORDER BY term does not match any column in the result set.

Upvotes: 1

Views: 1392

Answers (1)

gasman
gasman

Reputation: 25292

The specific() method on PageQuerySet works by running the initial query on the basic Page model as normal, then running additional queries - one for each distinct page type found in the results - to retrieve the information from the specific page models. This means it's not possible to use fields from the specific model in filter or order_by clauses, because those have to be part of the initial query, and at that point Django has no way to know which page models are involved.

However, if you know that your query should only ever return pages of one particular type (PhoenixPage in this case) containing the field you want to filter/order on, you can reorganise your query expression so that the query happens on that model instead:

PhoenixPage.objects.child_of(a).filter(tags=m).specific()

Upvotes: 3

Related Questions