2 votes

Django/Wagtail - moyen le plus efficace de raffiner l'ensemble de pages par plusieurs pages parentes

Je dispose d'une liste de pages de catégories obtenue comme suit :

category_ids = request.GET.getlist('category[]')
category_pages = MyCategoryPage.objects.filter(pk__in=category_ids)

Ce que j'aimerais faire, c'est affiner une liste de pages pour inclure uniquement celles qui sont des enfants des catégories spécifiées ci-dessus.

Je sais que vous pouvez le faire pour un objet comme suit :

pages = MySubPage.objects.child_of(category)

.. mais autant que je sache, il n'y a aucun moyen de faire child_of(category_pages) pour plus d'un parent.

Dans Django normal, où j'aurais probablement une ForeignKey sur MySubPage, je ferais simplement :

pages = MySubPage.objects.filter(category__in=filter_categories)

.. mais encore une fois, je ne suis pas sûr que je puisse le faire dans Wagtail.

Je sais que je pourrais probablement boucler à travers toutes les catégories et ajouter les sous-pages à une liste :

pages = []
for category in category_pages:
    pages.append(MySubPage.objects.child_of(category))

...puis les regrouper, mais cela ne semble pas très efficace et évidemment cela renverrait une liste au lieu d'une queryset.

J'ai cherché dans la documentation mais je n'ai pas trouvé de moyen plus direct d'atteindre cela.

3voto

GwynBleidD Points 10017

Pour résoudre cela, vous pouvez simplement regarder ce qui se passe en arrière-plan lorsque vous appelez child_of sur le queryset. Voici à quoi ressemble cette méthode et les méthodes de ce queryset qu'elle utilise (l'ordre de leur apparition dans le code n'est pas préservé):

    def child_of(self, other):
        """
        Filtre le QuerySet pour ne contenir que les pages qui sont des enfants directs de la page spécifiée.
        """
        return self.filter(self.child_of_q(other))

    def child_of_q(self, other):
        return self.descendant_of_q(other) & Q(depth=other.depth + 1)

    def descendant_of_q(self, other, inclusive=False):
        q = Q(path__startswith=other.path) & Q(depth__gte=other.depth)

        if not inclusive:
            q &= ~Q(pk=other.pk)

        return q

Comme vous pouvez le voir, wagtail renverra simplement un queryset avec quelques objets Q pour filtrer la liste des pages. Ce filtre est basé sur le chemin de la page parente et la profondeur. Pour recréer le même filtre, mais avec plusieurs parents, vous pouvez faire :

import operator

category_ids = request.GET.getlist('category[]')
pages = MySubPage.objects.filter(reduce(
    operator.or_,
    (
        Q(path__startswith=category.path) & Q(depth=category.depth + 1)
        for category in MyCategoryPage.objects.filter(pk__in=category_ids)
    )
))

Notez que j'ai optimisé la requête en supprimant Q(depth__gte=category.depth) de celle-ci, car cette condition sera remplie par Q(depth=category.depth + 1). Il n'a pas non plus besoin du ~Q(pk=category.pk) pour la même raison.

Notez également que cela nécessite 2 requêtes au total, ce qui peut être optimisé en une seule requête en utilisant intelligemment les paramètres path et depth des catégories directement dans vos paramètres GET.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X