fekioh
fekioh

Reputation: 996

How to specify the ordering of subpages in Wagtail admin

In Wagtail, consider the following page types:

class WorkshopPage(Page):
    category = models.ForeignKey(
    WorkshopCategory, on_delete=models.SET_NULL, blank=True, null=True
)

class WorkshopIndexPage(RoutablePageMixin, Page):
    subpage_types = ["WorkshopPage"]

In the wagtail admin, I need to order the subpages of WorkshopIndexPage by their category__name.

Since in the docs it says that setting ordering in a WorkshopPage Meta class will not work (it does not), I have tried the solution proposed here. However, when I do this inside my WorkshopIndexPage:

    def get_children(self):
        qs = WorkshopPage.objects.order_by("category__name")
        return qs

I get an error: Cannot combine queries on two different base models.

This seems like a simple use case but I can't seem to get it to work.

Upvotes: 1

Views: 96

Answers (2)

Jan
Jan

Reputation: 1

Try this code in wagtail_hooks.py:

@hooks.register('construct_explorer_page_queryset')
def apply_default_order_to_workshop_index_page(parent_page, pages, request):

    if parent_page.slug == 'workshops' and 'ordering' not in request.GET:
        pages = WorkshopPage.objects.child_of(parent_page).order_by('category__name')
    
    return pages

See this and this for more information.

Upvotes: 0

Tohid
Tohid

Reputation: 632

Currently there is no straight way to sort sub-pages in wagtail.

You have two options:

  1. Use inline models which is inline field for manytomany relationships.
  2. Add your own field for ordering sub-pages

Here is a quick list of changes to implement 2nd method:

  1. add a field to the page model for subpages to store an integer for sorting it's instances:
display_order = models.PositiveIntegerField(default=0)
  1. add meta class to the same model to indicate the field used for default ordering:
    class Meta:
        ordering = ["display_order"]
  1. add this field to content_panels to enable it in edit page.
  2. optional - implement the sort in front-end by overriding context:
def get_context(self, request):
    context = super().get_context(request)
    context['subpages'] = self.get_children().live().order_by('display_order')
    return context

Your code will probably look like this:

class WorkshopPage(Page):
    category = models.ForeignKey(
    WorkshopCategory, on_delete=models.SET_NULL, blank=True, null=True
)
    display_order = models.PositiveIntegerField(default=0)
    
    content_panels = Page.content_panels + [
        FieldPanel("display_order"),
    ]

    class Meta:
        ordering = ["display_order"]


class WorkshopIndexPage(RoutablePageMixin, Page):
    subpage_types = ["WorkshopPage"]

    def get_context(self, request):
        context = super().get_context(request)
        context['workshop_pages'] = self.get_children().live().order_by('display_order')
        return context

Upvotes: 0

Related Questions