Django-filter: (2.0) FilterView renvoyant toujours un QuerySet vide pour un FilterSet non lié.

Créé le 29 juin 2018  ·  19Commentaires  ·  Source: carltongibson/django-filter

J'ai une vue, AccountList , qui essaie de rendre une table django_table2. Le code source de la vue :

class AccountList(SingleTableMixin, FilterView):
    model = Account
    table_class = AccountTable
    template_name = 'accounts/account_list.html'
    context_table_name = 'object_list'
    ordering = ['vps']

    filterset_class = AccountFilter

Cette vue utilise actuellement cet ensemble de filtres (de django_filters) :

import django_filters
from accounts.models import Account

class AccountFilter(django_filters.FilterSet):
    class Meta:
        model = Account
        fields = ['is_suspended', 'is_abandoned']

    is_suspended = django_filters.BooleanFilter(name='is_suspended', initial='False')
    is_abandoned = django_filters.BooleanFilter(name='is_abandoned', initial='False')

    def __init__(self, data=None, *args, **kwargs):
        # if filterset is bound, use initial values as defaults
        if data is not None:
            # get a mutable copy of the QueryDict
            data = data.copy()

            for name, f in self.base_filters.items():
                initial = f.extra.get('initial')

                # filter param is either missing or empty, use initial as default
                if not data.get(name) and initial:
                    data[name] = initial

        super().__init__(data, *args, **kwargs)

En utilisant ce modèle :

{% if filter %}
    <form action="" method="get" class="form form-inline">
        {{ filter.form.as_p }}
        <input type="submit" />
    </form>
{% endif %}

{% render_table object_list %}

{% endblock %}

Ceci est mon de mon urls.py

path('', login_required(AccountList.as_view())),

Lorsque je visite ma page, 127.0.0.1:8000 , je constate que les filtres ne sont pas définis :
enter image description here

Mais si je fais 127.0.0.1:8000?page=1 , je vois que les filtres sont initialisés correctement :

enter image description here

Qu'est-ce qui fait que mes filtres n'ont pas de valeur par défaut lorsque je n'ai pas page=1 ajouté à mon URL ?

Bug

Commentaire le plus utile

Oui, j'ai commencé les relations publiques mais j'ai dû suspendre mon travail open source. Je devrais pouvoir reprendre là où je m'étais arrêté assez rapidement.

Tous les 19 commentaires

Essayez d'utiliser une valeur booléenne False réelle au lieu de la valeur de chaîne 'False' .

Alors quand j'ai essayé ce qui suit:

is_suspended = django_filters.BooleanFilter(name='is_suspended', initial=False)
is_abandoned = django_filters.BooleanFilter(name='is_abandoned', initial=False)

Voici comment les filtres sont rendus sur 127.0.0.1:8000/

image

Même si le filtre a été rendu avec les bonnes valeurs, le filtre n'a eu aucun effet car je peux toujours voir les comptes suspendus ou abandonnés.

De plus, lorsque je visite la page 2 en appuyant sur le bouton 2 en bas, j'arrive à http://127.0.0.1:8000/?page=2 avec les filtres réinitialisés à Unknown .

image

Voyez-vous une raison pour laquelle?

Désolé, rien d'évident ne me vient à l'esprit.

Pour le premier problème, je recommanderais de regarder la requête SQL résultante. Assurez-vous que les filtres suspended et abandoned sont effectivement appliqués.
Pour le deuxième problème, je vérifierais les données entrantes. Il est possible qu'une valeur non valide soit fournie aux filtres suspended et abandoned lors du changement de page.

J'ai inséré quelques déclarations d'impression et j'ai trouvé ce qui suit :

def __init__(self, data=None, *args, **kwargs):
    if data is not None:        # 1
        data = data.copy()

        for name, f in self.base_filters.items():
            initial = f.extra.get('initial')   

            # filter param is either missing or empty, use initial as default
            if not data.get(name) and initial:
                data[name] = initial

    super(BaseFilterSet, self).__init__(data, *args, **kwargs)

Il semble donc que data soit None . Saurez-vous pourquoi ma vue basée sur les classes ne passe pas data dans AccountFilter ?

Hum. L'argument data doit toujours être fourni par FilterView .

https://github.com/carltongibson/django-filter/blob/b1f1c6592ef64a2172ec39040607a9c0b0714140/django_filters/views.py#L39 -L46

data vide pour la première et la deuxième page ?

Ce n'est pas adressable tel quel. Fermeture en attendant suffisamment d'informations pour identifier un problème.

Même problème ici après la mise à niveau vers 2.0. Une page par défaut ou un filtre vide doit être présent dans l'URL pour obtenir les résultats. Est-ce au moins documenté quelque part ?

Encore besoin de plus d'infos pour pouvoir reproduire...

page ? N'est-ce pas lié à votre pagination plutôt qu'à Django Filter ?

Quelles informations sont nécessaires exactement ? La mise à niveau vers 2.0 conduit au comportement décrit. La rétrogradation vers la 1.1.0 ramène tout à la normale. J'ai essayé de simplifier la vue et le jeu de filtres pour qu'ils soient aussi minimalistes que possible, le comportement persiste, il semble donc que cela devrait être assez facile à reproduire.

@moorchegue , un exemple minimal de cas de test ou de projet de test qui démontre le problème serait utile.

Il semble également que le problème concerne tout ce qui fournit votre logique de pagination. Oui, il peut y avoir une certaine incompatibilité avec la nouvelle version, mais les problèmes de débogage dans d'autres packages sont hors de portée. Vous devez démontrer un bogue dans Django Filter pour que nous puissions faire quoi que ce soit ici.

OK, donc le problème ici est avec la méthode is_valid() introduite dans le cadre de #788.

https://github.com/carltongibson/django-filter/blob/1dde11c70eedeac32f6de92c426deb289e1aebd8/django_filters/filterset.py#L202 -L206

Cela renvoie automatiquement False lorsque data vaut None .

Dans la vue, nous définissons ensuite object_list sur qs.none() .

https://github.com/carltongibson/django-filter/blob/1dde11c70eedeac32f6de92c426deb289e1aebd8/django_filters/views.py#L80 -L83

D'où le comportement observé.

Le test pour cela n'échoue pas car le modèle de test utilise filter.qs plutôt que object_list .

https://github.com/carltongibson/django-filter/blob/1dde11c70eedeac32f6de92c426deb289e1aebd8/tests/templates/tests/book_filter.html#L3 -L5

La solution immédiate consiste à définir strict = False sur votre ensemble de filtres. Cela a pour conséquence que les paramètres de filtre non valides réels conduiront à un filtrage partiel plutôt que d'afficher des résultats vides, mais cela devrait être réalisable pour le moment. (Mieux vaut remplacer la logique de la vue get bien sûr...)

Nous allons réfléchir et améliorer le traitement du cas non relié.

Hum. Le déficit primaire est ici la disparité entre la vue de object_list et du filterSet .qs . Le retour du comportement strict au FilterSet.qs résoudrait ce problème.

De plus, il n'y a pas de gestion stricte/non stricte pour le backend DRF.

Je vais WIP ça.

@rpkilby Y a-t-il des progrès sur ce problème ?

Oui, j'ai commencé les relations publiques mais j'ai dû suspendre mon travail open source. Je devrais pouvoir reprendre là où je m'étais arrêté assez rapidement.

Notez que la solution de contournement devrait être de définir strict = False sur votre descendant FilterMixin (probablement le View .

Ne pouvez-vous pas simplement changer la condition pour retourner le qs en cas de forme illimitée ?

par exemple

-        if self.filterset.is_valid() or not self.get_strict():
+        if not self.filterset.is_bound or self.filterset.is_valid() or not self.get_strict():

https://github.com/carltongibson/django-filter/pull/1007

Merci.

Cette page vous a été utile?
0 / 5 - 0 notes