Django-filter: (2.0) FilterView gibt immer leeres QuerySet für ungebundenes FilterSet zurück.

Erstellt am 29. Juni 2018  ·  19Kommentare  ·  Quelle: carltongibson/django-filter

Ich habe die Ansicht AccountList , die versucht, eine django_table2-Tabelle zu rendern. Der Quellcode der Ansicht:

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

Diese Ansicht verwendet derzeit dieses Filterset (von 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)

Verwenden dieser Vorlage:

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

{% render_table object_list %}

{% endblock %}

Dies ist meine von meiner urls.py

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

Wenn ich meine Seite 127.0.0.1:8000 besuche, sehe ich, dass die Filter nicht gesetzt sind:
enter image description here

Aber wenn ich dann 127.0.0.1:8000?page=1 mache, sehe ich, dass die Filter richtig initialisiert sind:

enter image description here

Was führt dazu, dass meine Filter keinen Standardwert haben, wenn ich nicht page=1 an meine URL angehängt habe?

Bug

Hilfreichster Kommentar

Ja - begann mit der PR, musste aber meine Open-Source-Arbeit auf Eis legen. Ich sollte bald da weitermachen können, wo ich aufgehört habe.

Alle 19 Kommentare

Versuchen Sie es mit einem tatsächlichen False booleschen Wert anstelle des Zeichenfolgenwerts 'False' .

Also als ich folgendes probiert habe:

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

So werden die Filter auf 127.0.0.1:8000/ gerendert

image

Obwohl der Filter mit den richtigen Werten gerendert wurde, hatte der Filter keine Wirkung, da ich immer noch Konten sehen kann, die gesperrt oder aufgegeben wurden.

Außerdem, wenn ich Seite 2 durch Drücken der Schaltfläche 2 unten besuche, komme ich zu http://127.0.0.1:8000/?page=2 wobei die Filter auf Unknown .

image

Sehen Sie einen Grund dafür?

Entschuldigung - mir fällt nichts Offensichtliches ein.

Für das erste Problem würde ich empfehlen, sich die resultierende SQL-Abfrage anzusehen. Stellen Sie sicher, dass die Filter suspended und abandoned tatsächlich angewendet werden.
Für das zweite Problem würde ich die eingehenden Daten überprüfen. Es ist möglich, dass den Filtern suspended und abandoned beim Seitenwechsel ein ungültiger Wert bereitgestellt wird.

Ich habe ein paar Druckanweisungen eingefügt und folgendes gefunden:

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)

Es scheint also, dass data None . Wissen Sie zufällig, warum meine klassenbasierte Ansicht data an AccountFilter übergibt?

Hm. Das Argument data sollte immer von FilterView bereitgestellt werden.

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

Ist data sowohl für die erste als auch für die zweite Seite leer?

Dies ist so wie es ist nicht adressierbar. Es werden genügend Informationen geschlossen, um ein Problem zu identifizieren.

Gleiches Problem hier nach dem Upgrade auf 2.0. In der URL sollte entweder eine Standardseite oder ein leerer Filter vorhanden sein, um die Ergebnisse zu erhalten. Ist das zumindest irgendwo dokumentiert?

Brauche noch mehr Infos um reproduzieren zu können...

page ? Hat das nicht mit Ihrer Paginierung zu tun, und nicht mit dem Django-Filter?

Welche Informationen werden genau benötigt? Ein Upgrade auf 2.0 führt zu dem beschriebenen Verhalten. Durch ein Downgrade auf 1.1.0 wird alles wieder normal. Ich habe versucht, die Ansicht und den Filtersatz so minimalistisch wie möglich zu vereinfachen, das Verhalten bleibt bestehen, daher scheint es ziemlich einfach zu sein, dies zu reproduzieren.

@moorchegue , ein minimaler Beispieltestfall oder ein Testprojekt, das das Problem demonstriert, wäre hilfreich.

Es sieht auch so aus, als ob das Problem bei dem liegt, was Ihre Paginierungslogik bereitstellt. Ja, es kann eine gewisse Inkompatibilität mit der neuen Version geben, aber das Debuggen von Problemen in anderen Paketen liegt außerhalb des Umfangs. Sie müssen einen Fehler im Django-Filter demonstrieren, damit wir hier etwas tun können.

OK, das Problem liegt hier also bei der Methode is_valid() , die als Teil von #788 eingeführt wurde.

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

Dies gibt automatisch False wenn data None .

In der Ansicht setzen wir dann object_list auf qs.none() .

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

Daher das beobachtete Verhalten.

Der Test dafür schlägt nicht fehl, weil die Testvorlage filter.qs anstelle von object_list .

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

Die sofortige Abhilfe besteht darin, strict = False für Ihr Filterset festzulegen. Dies hat zur Folge, dass tatsächlich ungültige Filterparameter zu einer teilweisen Filterung führen, anstatt leere Ergebnisse anzuzeigen, aber es sollte für den Moment praktikabel sein. (Besser ist es natürlich, die Logik der Ansicht get zu überschreiben...)

Wir werden uns Gedanken machen und die Handhabung für den ungebundenen Fall verbessern.

Hm. Der Hauptmangel hier ist die Diskrepanz zwischen object_list der Ansicht und .qs des FilterSets. Das strikte Verhalten zurück zu FilterSet.qs würde das beheben.

Außerdem gibt es keine strikte/nicht-strenge Behandlung für das DRF-Backend.

Ich werde das aufräumen.

@rpkilby Gibt es Fortschritte bei diesem Thema?

Ja - begann mit der PR, musste aber meine Open-Source-Arbeit auf Eis legen. Ich sollte bald da weitermachen können, wo ich aufgehört habe.

Beachten Sie, dass die Problemumgehung darin bestehen sollte, strict = False für Ihren FilterMixin Nachkommen festzulegen (wahrscheinlich die View .

Können Sie nicht einfach die Bedingung ändern, um die qs im Falle einer unbegrenzten Form zurückzugeben?

z.B

-        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

Vielen Dank.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen