Reputation: 497
I'm using django-filter together with DRF. I have a favourite
-model, which is linked to several other models through a GenericRelation.
To filter for entries which have a favourite-flag, I've created a custom FavouriteFilter
, which I add to the respective model. I would like to query for the content_type_id
of the respective model in order to limit the results from Favourite
. However, I don't know how I can pass down the model
to the filter-method in the FavouriteFilter
.
Here's a code snippet to illustrate the issue:
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
In this example, the <model>
-attribute is missing. How can I pass down this information from Project
to the filter?
Upvotes: 0
Views: 990
Reputation: 497
Keyword arguments can be passed down to the filter, but they need to be removed from the kwarg-dict before the super()-method is called. Otherwise they get passed on to the superclass, the superclass's __init__()
-method doesn't know the keyword and a TypeError
is thrown:
TypeError: __init__() got an unexpected keyword argument 'model'
In the example above, the superclass is django_filters.BooleanFilter
respectively django_filters.Filter
.
Using the dict.pop()
-method, the keyword is removed from the kwargs-dictionary and at the same time we can save it for further use. Since content_type
never changes after initialization, it can already be set in __init__()
.
Here's a working example of the code above, where Project
is the django-model I want to pass down to the filter:
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
For my specific use-case, where I'm looking for the model that is using the filter, the model is available through the queryset as qs.model
. The code-snippet looks like this:
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
Upvotes: 1