#python #django #django-filter #drf-queryset
#python #django #django-filter #drf-набор запросов
Вопрос:
Я использую django-filter вместе с DRF. У меня есть favourite
-модель, которая связана с несколькими другими моделями через GenericRelation. Для фильтрации записей с флагом избранного я создал пользовательский FavouriteFilter
, который я добавляю в соответствующую модель. Я хотел бы запросить content_type_id
соответствующую модель, чтобы ограничить результаты от Favourite
. Однако я не знаю, как я могу передать model
методу filter в FavouriteFilter
.
Вот фрагмент кода, иллюстрирующий проблему:
class ProjectFilter(BaseFilter):
favourite_only = FavouriteFilter()
class FavouriteFilter(django_filters.BooleanFilter):
"""
A custom filter which returns a users favourites of an element
"""
def __init__(self, *args, **kwargs):
# gettext_lazy breaks the OpenAPI generation => use gettext instead
kwargs['label'] = gettext("My favourites")
super(FavouriteFilter, self).__init__(*args, **kwargs)
def filter(self, qs, value):
if value == True:
user = get_current_user()
content_type = ContentType.objects.get_for_model(<model>)
return qs.filter(pk__in=Favourite.objects
.filter(owner_id=user)
.filter(content_type_id=content_type)
.values_list('object_id', flat=True)
)
else:
return qs
В этом примере <model>
-атрибут отсутствует. Как я могу передать эту информацию из Project
в фильтр?
Ответ №1:
Аргументы ключевого слова могут быть переданы фильтру, но они должны быть удалены из kwarg-dict перед вызовом super()-метода. В противном случае они передаются суперклассу, __init__()
-метод суперкласса не знает ключевое слово и TypeError
выдается:
TypeError: __init__() got an unexpected keyword argument 'model'
В приведенном выше примере суперкласс является django_filters.BooleanFilter
соответственно django_filters.Filter
.
Используя dict.pop()
-метод, ключевое слово удаляется из kwargs-dictionary и в то же время мы можем сохранить его для дальнейшего использования. Поскольку content_type
он никогда не изменяется после инициализации, он уже может быть установлен в __init__()
.
Вот рабочий пример приведенного выше кода, где Project
— модель django, которую я хочу передать фильтру:
class ProjectFilter(BaseFilter):
favourite_only = FavouriteFilter(model=Project)
class FavouriteFilter(django_filters.BooleanFilter):
"""
A custom filter which returns a users favourites of an element
"""
def __init__(self, *args, **kwargs):
# gettext_lazy breaks the OpenAPI generation => use gettext instead
kwargs['label'] = gettext("My favourites")
model = kwargs.pop('model')
self.content_type = ContentType.objects.get_for_model(model)
super(FavouriteFilter, self).__init__(*args, **kwargs)
def filter(self, qs, value):
if value == True:
user = get_current_user()
return qs.filter(pk__in=Favourite.objects
.filter(owner_id=user)
.filter(content_type_id=self.content_type)
.values_list('object_id', flat=True)
)
else:
return qs
Для моего конкретного варианта использования, когда я ищу модель, использующую фильтр, модель доступна через набор запросов как qs.model
. Фрагмент кода выглядит следующим образом:
class ProjectFilter(BaseFilter):
favourite_only = FavouriteFilter()
class FavouriteFilter(django_filters.BooleanFilter):
"""
A custom filter which returns a users favourites of an element
"""
def __init__(self, *args, **kwargs):
# gettext_lazy breaks the OpenAPI generation => use gettext instead
kwargs['label'] = gettext("My favourites")
super(FavouriteFilter, self).__init__(*args, **kwargs)
def filter(self, qs, value):
if value == True:
user = get_current_user()
content_type = ContentType.objects.get_for_model(qs.model)
return qs.filter(pk__in=Favourite.objects
.filter(owner_id=user)
.filter(content_type_id=content_type)
.values_list('object_id', flat=True)
)
else:
return qs