Django-filter: FilterSet内のクエリセットの取得をオーバーライドできるようにする

作成日 2017年06月21日  ·  4コメント  ·  ソース: carltongibson/django-filter

タイトルは少し紛らわしいかもしれません。 クエリセットが取得され、フィールドのフィルタリングが適用されるたびに呼び出される汎用のfilterメソッドがあると便利です。 ええ、 qsプロパティをオーバーライドし、最初にフィルタリングされたクエリセットを取得してから、プリフェッチを適用できます。 しかし、_これに何らかの副作用があり、フィルターに渡されたデータに安全にアクセスできるかどうかは明らかではありません。_

私のユースケースがあります。
M2M関係でクエリセットをフィルタリングしています( queryset.filter(items__field="foo") )。 ただし、プリフェッチされたアイテムもそのフィールドでフィルタリングしたいと思います。

言い換えれば、私のフィルターは次のようになります。

# In my filterset
def filter_items_by_field(self, queryset, name, value):
        queryset = queryset.all().filter(
            items__field=value,
        )
        prefetched_items = Prefetch(
            'items',
            Item.objects..filter(
                field=value,
            ),
        )
        return queryset.prefetch_related(prefetched_items)

# In my vieset
def get_queryset(self):
        queryset = super().get_queryset()
        # This should be applied everytime
        prefetched_items = Prefetch(
            'items',
            Item.objects..filter(
                another_field=some_value,
            ),
        )
        return queryset.prefetch_related(prefetched_items)

このような単一のフィルターは期待どおりに機能します。 ただし、複数のfilter_items_by_*フィルターがある場合があります+すべてのフィルターのプリフェッチからクエリセットに同じフィルターを適用する必要があります。 私の場合、複数のフィルターは相互に排他的であるため、問題にはなりません。 ただし、フィルター+ジェネリックは例外を引き起こします。

重要なのは、同じルックアップに対して1つのプリフェッチしか適用できないということです。 したがって、私の場合は、単に例外が発生します。

この問題の一時的な回避策があります。すべてのfilter_items_by_*は、プリフェッチで必須のフィルタリングを実行します。
また、 filter_queryset (フィルタリングが適用された後)で、 itemsがプリフェッチされていない場合(クエリセットで保護された属性を検査しているので何が悪いのか)、必須のフィルタリングを使用してプリフェッチを適用します。 しかし、このような状況に対処するためのより良い方法があるはずです。

私の例が十分に明確であることを願っています。

最も参考になるコメント

こんにちは@ Alexx-G。 2.0リリースはまだ道のりですが、この質問に具体的に対処するAPIにいくつかの変更が加えられる予定です。 要するに、

  • .filter_queryset()メソッドをオーバーライドする必要があります。 .qs属性は、基本的に.filter_queryset()を呼び出し、その結果をキャッシュする役割を果たします。
  • プリフェッチ用のフィルター値はself.form.cleaned_dataから取得できます。

全てのコメント4件

こんにちは@ Alexx-G。 これは、フィルターセットと関連するフィルタークラスの間の調整によって可能になると思います。 基本的に、フィルターはPrefetchインスタンスをそのparentフィルターセットに追加します。 FilterSet.qsは、必要に応じてprefetch_related呼び出すだけです。

そうは言っても、これのユースケースは何ですか? プリフェッチを処理するためのより適切な場所がある可能性が高いと私は主張します。

これは、フィルターセットと関連するフィルタークラスの間の調整によって可能になると思います。

qsプロパティをオーバーライドすることで問題を解決しましたが、繰り返しになりますが、それは明らかではなく、文書化されていません。 言い換えると、基本クラスの内部変更がフィルターセットに影響を与えないかどうかはわかりません(バージョンを固定しましたが、とにかく..)

そうは言っても、これのユースケースは何ですか? プリフェッチを処理するためのより適切な場所がある可能性が高いと私は主張します。

上記のユースケースについて説明したと思います。 プリフェッチはフィルターに依存するため、フィルタリングとプリフェッチを分離することは意味がありません。 そして、それらを分離する場合、フィルターに対して同じチェックを複製する必要があります。

言い換えると、qsプロパティのパブリックコントラクトの説明またはそのドキュメント化されたフックがある場合、これによりフィルターセットのカスタマイズが向上します。

こんにちは@ Alexx-G。 2.0リリースはまだ道のりですが、この質問に具体的に対処するAPIにいくつかの変更が加えられる予定です。 要するに、

  • .filter_queryset()メソッドをオーバーライドする必要があります。 .qs属性は、基本的に.filter_queryset()を呼び出し、その結果をキャッシュする役割を果たします。
  • プリフェッチ用のフィルター値はself.form.cleaned_dataから取得できます。

あなたの努力に感謝します!

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