Передать дополнительный атрибут django-filter

#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