2017-08-11 11 views
0

documented hereと同様の方法でDjango-Rest-Framework-Filtersを使用しています。カスタムPostQuerySetクエリーセットメソッドを使用している関連Postクラスのいくつかの条件でAuthorをフィルタリングしたいと思います。Django-Rest-Framework-Filters:RelatedFilterでフィルタが使用されているカスタムクエリセットメソッドを使用しています

class PostFilter(filters.FilterSet): 
    myfilter = filters.BooleanFilter(name='date_published', method='filter_myfilter') 

    class Meta: 
     model = Post 
     fields = ['title', 'content'] 

    def filter_myfilter(self, qs, name, value): 
     """ 
     Calls myqueryset_method defined on PostQuerySet 
     """ 
     return qs.myqueryset_method() 

class AuthorFilter(filters.FilterSet): 
    posts = filters.RelatedFilter('PostFilter', queryset=Post.objects.all()) 

    class Meta: 
     model = Author 
     fields = ['name'] 

トラブルで、著者のAPIの一部として、このフィルタを使用しようとすると、例えば:フィルター、myfilterは、としてPostFilterフィルタセットに定義されています

"AttributeError: 'Manager' object has no attribute 'myqueryset_method'" 

それは直感に反するようだが、それはRelatedFilterによって呼び出された後のクエリセットではありませんので、あなたがqs arugmentにPostQuerySetメソッドを実行することはできません表示されます。

/api/authors?posts__myfilter=true 

エラーがスローされます。ドキュメントで説明したように:

[when making the filter calls] 

/api/posts?is_published=true 
/api/authors?posts__is_published=true 

"In the first API call, the filter method receives a queryset of posts. In the second, it receives a queryset of users." 

それでは、どのように活用しカスタムクエリセットメソッドRelatedFilterによって消費されているフィルタでをできますか?

+1

これは現時点で実現可能ではなく、[#99](https://github.com/philipn/django-rest-framework-filters/issues/99)の解決策に依存しています。 。私はしばらくの間、この問題でゆっくりと目を奪われましたが、パフォーマンスの問題にぶつかりました。 – Sherpa

答えて

0

discussionに記載されている「チェーンネストされた結合」(q3)のセマンティクスを実現し、この問題を回避することができました。sherpaがリンクしています。

以下のコードは少し粗いですが、モデルタイプが同じ場合(qs.model == filterset._meta.model)基本的にはフィルタをqsにチェーンします。

これらが異なる場合、(Author)qsのネストされた結合を効果的に実行します。 utils.pyで

:filters.pyで

from django.db.models.constants import LOOKUP_SEP 

def get_lookup_expression(name): 

    # The name argument contains the filter to apply. NOTE - if this filter is consumed via a RelatedFilter, 
    # i.e. on AuthorFilter, the filters list will contain ['author', 'filter_myfilter'] 
    filters = name.split(LOOKUP_SEP) 

    # Remove the final filter name from filter list -this should handle filtering over multiple traversed models 
    filter = LOOKUP_SEP.join(filters[:-1]) 

    # Create new lookup expression, i.e. 'author__in' 
    lookup_expr = LOOKUP_SEP.join([filter, 'in']) 

    return lookup_expr 


def related_filter_handler(func, filterset, qs, name, value): 

    # Get model in the queryset and the model defined in the filterset 
    qs_model = qs.model 
    filter_model = filterset._meta.model 

    if (qs_model == filter_model): 
     # If they are equal then we are applying the filter directly on the model via its API, e.g. '/api/posts?customfilter=true' 
     return func(qs, value) 
    else: 
     # They will be different when calling '/api/author?post__customfilter=true' 

     # Apply filter to filter_model 
     T2 = func(filter_model.objects.all(), value) 

     # Construct lookup_expr 'X__in' where X is the path to traverse from the related (qs) model 
     lookup_expr = get_lookup_expression(name) 

     # Filters RelatedFilter qs, i.e. qs.filter(post__in=T2) 
     return qs.filter(**{lookup_expr: T2}) 

import rest_framework_filters as filters 
from .utils import related_filter_handler 
from .models import Post 

class PostFilter(filters.FilterSet): 
    myfilter = filters.BooleanFilter(name='date_published', method='customfilter') 

    class Meta: 
     model = Post 
     fields = ['title', 'content'] 

    def customfilter(self, qs, name, value): 
     """ 
     Calls myqueryset_method defined on PostQuerySet 
     """ 
     # Wrap the filter logic in function/lambda 
     def apply(qs, value): 
      # e.g. passing in value to the queryset method 
      return qs.myqueryset_method(value) 

     # Pass filter expression into helper function which handles chaining the filter as is or chaining a nested join 
     return related_filter_handler(apply, self, qs, name, value) 

仕事は周りの私のユニットテストを渡します。しかし、あなたがプロキシモデルを扱っているなら、それは問題に遭遇するかもしれません。コードを改善するために自由に編集してください。

RelatedFiltersを使用して未来/未知の使用に対してカスタムフィルタメソッドをすべてコーディングする必要があるため、これがRelatedFilterのネイティブ動作であれば、もっと良いでしょう。

関連する問題