Django-filter: (2.0)FilterViewは、バインドされていないFilterSetに対して常に空のQuerySetを返します。

作成日 2018年06月29日  ·  19コメント  ·  ソース: carltongibson/django-filter

django_table2テーブルをレンダリングしようとしているビューAccountListがあります。 ビューのソースコード:

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

このビューは現在、(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)

このテンプレートの使用:

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

{% render_table object_list %}

{% endblock %}

これは私のurls.pyからのものです

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

自分のページ127.0.0.1:8000にアクセスすると、フィルターが設定されていないことがわかります。
enter image description here

しかし、 127.0.0.1:8000?page=1を実行すると、フィルターが正しく初期化されていることがわかります。

enter image description here

URLにpage = 1を追加していないのに、フィルターにデフォルト値がない原因は何ですか?

Bug

最も参考になるコメント

うん-PRを始めたが、私のオープンソースの仕事を保留にしなければならなかった。 中断したところからすぐに再開できるはずです。

全てのコメント19件

文字列値'False'代わりに、実際のFalseブール値を使用してみてください。

だから私が以下を試したとき:

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

これは、フィルターが127.0.0.1:8000/レンダリングされる方法です。

image

フィルタが正しい値でレンダリングされたとしても、一時停止または放棄されたアカウントを確認できるため、フィルタは効果がありませんでした。

さらに、下部にある2ボタンを押してページ2にアクセスすると、フィルターがUnknownリセットされた状態でhttp://127.0.0.1:8000/?page=2に到達します。

image

理由はわかりますか?

申し訳ありませんが、明らかなことは何も思い浮かびません。

最初の問題については、結果のSQLクエリを確認することをお勧めします。 suspendedおよびabandonedフィルターが実際に適用されていることを確認してください。
2番目の問題では、受信データを確認します。 ページを変更するときに、 suspendedおよびabandonedフィルターに無効な値が指定されている可能性があります。

いくつかのprintステートメントを挿入したところ、次のことがわかりました。

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)

したがって、 dataNoneです。 私のクラスベースのビューがdataAccountFilter渡さない理由を知っていますか?

うーん。 data引数は、常にFilterView指定する必要があります。

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

1ページ目と2ページ目の両方でdata空ですか?

これはそのままでは対処できません。 問題を特定するのに十分な情報が保留中です。

2.0にアップグレードした後も同じ問題が発生します。 結果を取得するには、デフォルトページまたは空のフィルターのいずれかがURLに存在する必要があります。 これは少なくともどこかに文書化されていますか?

再現するには、さらに多くの情報が必要です...

page ? これは、Django Filterではなく、ページ付けと関係がありませんか?

正確にどのような情報が必要ですか? 2.0にアップグレードすると、説明されている動作になります。 1.1.0にダウングレードすると、すべてが正常に戻ります。 ビューとフィルターセットをできるだけ最小限に単純化しようとしましたが、動作は持続するため、これはかなり簡単に再現できるはずです。

@moorchegue 、問題を実証する最小限のサンプルテストケースまたはテストプロジェクトが役立ちます。

また、問題はページ付けロジックを提供しているものにあるようです。 はい、新しいバージョンとの互換性がない可能性がありますが、他のパッケージのデバッグの問題は範囲外です。 ここでできることを行うには、DjangoFilterのバグを示す必要があります。

さて、ここでの問題は、#788の一部として導入されたis_valid()メソッドにあります。

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

dataNone場合、これは自動的にFalse返します。

ビューでは、 object_listqs.none()ます。

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

したがって、観察された動作。

このためのテストは、テストテンプレートを使用しているので、失敗していませんfilter.qsではなくobject_list

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

差し迫った回避策は、フィルターセットにstrict = Falseを設定することです。 これは、実際の無効なフィルターパラメータが空の結果を表示するのではなく、部分的なフィルタリングにつながるという結果をもたらしますが、今のところは機能するはずです。 (もちろん、ビューgetロジックをオーバーライドすることをお勧めします...)

バインドされていないケースの処理について考え、改善します。

うーん。 ここでの主な欠点は、ビューのobject_listとFilterSetの.qs間の不一致です。 厳密な動作をFilterSet.qs戻すと、それが修正されます。

さらに、DRFバックエンドには厳密/非厳密な処理はありません。

これを拭きます。

@rpkilbyこの問題に進展はありますか?

うん-PRを始めたが、私のオープンソースの仕事を保留にしなければならなかった。 中断したところからすぐに再開できるはずです。

回避策は、 FilterMixin子孫(おそらくView strict = Falseを設定することであることに注意してください。

無制限のフォームの場合にqsを返すように条件を変更することはできませんか?

例えば

-        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

ありがとう。

このページは役に立ちましたか?
0 / 5 - 0 評価