1

これは私が使用しているフィルタセットのやや簡略化した例です。これはDjango Rest FrameworkのDjangoFilterBackendで使用しています。 /api/bookmarks/?title__contains=word1&title__contains=word2にリクエストを送信し、両方の単語を含む結果が返されますが、現在は最初のパラメータは無視され、word2のフィルタのみが無視されます。DRFを使用したDjango-filter - 同じルックアップで複数の値を適用するときに 'と'を実行する方法?

ご協力いただければ幸いです。

class BookmarkFilter(django_filters.FilterSet): 

    class Meta: 
     model = Bookmark 
     fields = { 
      'title': ['startswith', 'endswith', 'contains', 'exact', 'istartswith', 'iendswith', 'icontains', 'iexact'], 
     } 

class BookmarkViewSet(viewsets.ModelViewSet): 
    serializer_class = BookmarkSerializer 
    permission_classes = (IsAuthenticated,) 
    filter_backends = (DjangoFilterBackend,) 
    filter_class = BookmarkFilter 
    ordering_fields = ('title', 'date', 'modified') 
    ordering = '-modified' 
    page_size = 10 
+0

定義済みのフィルタバックエンドを使用するビューコードを貼り付けることもできます。 – shady

+0

ここで問題を確認してください。フィールドの値のセット(または値の間のOR)をフィルタする(https://github.com/carltongibson/django-filter/issues/137#issuecomment-77697870) – shady

+0

@shadそのスレッドを読んでください。リンクしたコードは、値間の論理和を求めるのにANDは使用できません。上に掲示されたビューコード。助けてくれてありがとう。 – ergusto

答えて

4

主な問題は、複数の値を操作する方法を理解するフィルタが必要なことです。基本的に2つのオプションがあります。

  • 使用MultipleChoiceFilter(この場合にはお勧めしません)
  • が、これはあなたを保持しながらMultipleChoiceFilter

    class BookmarkFilter(django_filters.FilterSet): 
        title__contains = django_filters.MultipleChoiceFilter(
         name='title', 
         lookup_expr='contains', 
         conjoined=True, # uses AND instead of OR 
         choices=[???], 
        ) 
    
        class Meta: 
         ... 
    

    を使用して

カスタムフィルタクラスを書きますあなたが選択肢のリストを構築しなければならないという問題があります。可能な選択肢を単純化/縮小できるかどうかはわかりませんが、データベースからすべてのタイトルを取り出し、タイトルを別の単語に分割し、重複を削除するセットを作成する必要があるようです。これは、あなたが持っているレコードの数によっては高価な/遅いようです。

カスタムFilter

また、カスタムフィルタクラスを作成することができます - 以下のようなもの:

class MultiValueCharFilter(filters.BaseCSVFilter, filters.CharFilter): 
    def filter(self, qs, value): 
     # value is either a list or an 'empty' value 
     values = value or [] 

     for value in values: 
      qs = super(MultiValueCharFilter, self).filter(qs, value) 

     return qs 


class BookmarkFilter(django_filters.FilterSet): 
    title__contains = MultiValueCharFilter(name='title', lookup_expr='contains') 

    class Meta: 
     ... 

使用(値はカンマで区切られていることに注意してください):

GET /api/bookmarks/?title__contains=word1,word2 

結果:

qs.filter(title__contains='word1').filter(title__contains='word2') 

シンタックスは少し変更されましたが、CSVベースのフィルタでは不要な選択肢を構成する必要はありません。

?title__contains=word1&title__contains=word2構文をサポートすることは、ウィジェットが適切なHTML入力をレンダリングできないため、実際にはサポートされていないことに注意してください。同じname属性を持つ追加のテキスト入力を追加/削除するには、SelectMultiple(選択肢が必要です)を使用するか、クライアント上でjavascriptを使用する必要があります。


フィルタとフィルタセットはあまり詳しく説明しませんが、Djangoのフォームの拡張です。 Filter

  • は、順番にWidgetを有するフォームFieldを有しています。
  • FilterSetは、Filterで構成されています。
  • FilterSetは、フィルタのフィールドに基づいて内部フォームを生成します。各フィルタ成分の

責任:

  • ウィジェットはdataQueryDictからの生の値を取得します。
  • フィールドは、生の値を検証します。
  • フィルタは、有効な値を使用して、クエリセットへの呼び出しを構成します(filter())。

同じフィルタに複数の値を適用するには、複数の値を操作する方法を理解するフィルタ、フィールド、ウィジェットが必要です。


カスタムフィルタは、順番に合成フィールドとウィジェットクラスに「コンマ分離=>リスト」機能で混合れ、BaseCSVFilterに混合することによってこれを達成します。

私はCSVのミックスインのソースコードを見てお勧めしますが、要するにたい:

  • widgetは、値のリストに入ってくる値を分割します。
  • fieldは、「メイン」フィールドクラス(たとえば、CharFieldまたはIntegerFieldなど)の個々の値を検証することによって、値のリスト全体を検証します。このフィールドは、混合されたウィジェットを派生させます。
  • filterは、混在したフィールドクラスを派生します。

CSVフィルタは、値リストを受け入れるinおよびrangeルックアップで使用することを目的としていました。この場合、containsには1つの値が必要です。 filter()メソッドは、値の反復処理と個々のフィルタ呼び出しの連鎖によってこれを修正します。

+0

まずは、このような徹底的な投稿に感謝します。それは私があいまいな理解を持っていた多くのものをクリアしました。あなたのMultiValueCharFilterを実装しようとしましたが、残念なことにコンマ構文を使用して複数の値でフィルタリングしても0の結果が返されましたが、エラーはありませんでした。 1つの値でフィルタリングするとうまくいきました。私はソースを見て、それがうまくいくはずだから、何が間違っているのか分からない。 CharFilterの代わりに他のほとんどのタイプのフィルタでもうまくいくと思っていますか?再び、多くのありがとう。 – ergusto

+0

ええ、それは私の頭の上からほぼ正しいはずのものだった。私は実際にそれをテストする時間がかかりませんでした。あなたは 'NumberFilter'のような他のタイプのフィルタでもうまくいくはずです。それは実際には理にかなっているところはほんの少しです。 – Sherpa

+0

残念ながら、タイトルにコンマが含まれていると、BaseCSVFilterのソリューションが正しく動作しません – kalombo

0

あなたは、このようなカスタムリストフィールドの何かを作成することができます

from django.forms.widgets import SelectMultiple 
from django import forms 

class ListField(forms.Field): 
    widget = SelectMultiple 

    def __init__(self, field, *args, **kwargs): 
     super(ListField, self).__init__(*args, **kwargs) 
     self.field = field 

    def validate(self, value): 
     super(ListField, self).validate(value) 
     for val in value: 
      self.field.validate(val) 

    def run_validators(self, value): 
     for val in value: 
      self.field.run_validators(val) 

    def to_python(self, value): 
     if not value: 
      return [] 
     elif not isinstance(value, (list, tuple)): 
      raise ValidationError(self.error_messages['invalid_list'], code='invalid_list') 
     return [self.field.to_python(val) for val in value] 

とMultipleChoiceFilterを使用してカスタムフィルタを作成します。その後

class ContainsListFilter(django_filters.MultipleChoiceFilter): 
    field_class = ListField 

    def get_filter_predicate(self, v): 
     name = '%s__contains' % self.name 
     try: 
      return {name: getattr(v, self.field.to_field_name)} 
     except (AttributeError, TypeError): 
      return {name: v} 

カスタムフィルタとフィルタセットを作成することができます。

from django.forms import CharField 

class StorageLocationFilter(django_filters.FilterSet): 
    title_contains = ContainsListFilter(field=CharField()) 

私のために働く。それがあなたに役立つことを願っています。

関連する問題