From 4bdcf054ebdb29e84ea190bb6becc8dc27ca286d Mon Sep 17 00:00:00 2001 From: Bas Stottelaar Date: Fri, 28 Jul 2017 16:46:39 +0200 Subject: [PATCH] Pass context object to FilterSet instance to support request-baed filtering (fixes #203). --- docs/filtering.rst | 20 ++++++++++ graphene_django/filter/fields.py | 3 +- graphene_django/filter/tests/test_fields.py | 42 +++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/docs/filtering.rst b/docs/filtering.rst index f6ad882..2e6b87f 100644 --- a/docs/filtering.rst +++ b/docs/filtering.rst @@ -126,3 +126,23 @@ create your own ``Filterset`` as follows: # We specify our custom AnimalFilter using the filterset_class param all_animals = DjangoFilterConnectionField(AnimalNode, filterset_class=AnimalFilter) + +The context argument is passed on as the `request argument `__ +in a ``django_filters.FilterSet`` instance. You can use this to customize your +filters to be context-dependent. We could modify the ``AnimalFilter`` above to +pre-filter animals owned by the authenticated user (set in ``context.user``). + +.. code:: python + + class AnimalFilter(django_filters.FilterSet): + # Do case-insensitive lookups on 'name' + name = django_filters.CharFilter(lookup_type='iexact') + + class Meta: + model = Animal + fields = ['name', 'genus', 'is_domesticated'] + + @property + def qs(self): + # The query context can be found in self.request. + return super(AnimalFilter, self).filter(owner=self.request.user) diff --git a/graphene_django/filter/fields.py b/graphene_django/filter/fields.py index fc414bf..68a9072 100644 --- a/graphene_django/filter/fields.py +++ b/graphene_django/filter/fields.py @@ -73,7 +73,8 @@ class DjangoFilterConnectionField(DjangoConnectionField): filter_kwargs = {k: v for k, v in args.items() if k in filtering_args} qs = filterset_class( data=filter_kwargs, - queryset=default_manager.get_queryset() + queryset=default_manager.get_queryset(), + request=context ).qs return super(DjangoFilterConnectionField, cls).connection_resolver( diff --git a/graphene_django/filter/tests/test_fields.py b/graphene_django/filter/tests/test_fields.py index 1b24ff2..565e4f2 100644 --- a/graphene_django/filter/tests/test_fields.py +++ b/graphene_django/filter/tests/test_fields.py @@ -136,6 +136,48 @@ def test_filter_shortcut_filterset_extra_meta(): assert 'headline' not in field.filterset_class.get_fields() +def test_filter_shortcut_filterset_context(): + class ArticleContextFilter(django_filters.FilterSet): + + class Meta: + model = Article + exclude = set() + + @property + def qs(self): + qs = super(ArticleContextFilter, self).qs + return qs.filter(reporter=self.request.reporter) + + class Query(ObjectType): + context_articles = DjangoFilterConnectionField(ArticleNode, filterset_class=ArticleContextFilter) + + r1 = Reporter.objects.create(first_name='r1', last_name='r1', email='r1@test.com') + r2 = Reporter.objects.create(first_name='r2', last_name='r2', email='r2@test.com') + Article.objects.create(headline='a1', pub_date=datetime.now(), reporter=r1, editor=r1) + Article.objects.create(headline='a2', pub_date=datetime.now(), reporter=r2, editor=r2) + + class context(object): + reporter = r2 + + query = ''' + query { + contextArticles { + edges { + node { + headline + } + } + } + } + ''' + schema = Schema(query=Query) + result = schema.execute(query, context_value=context()) + assert not result.errors + + assert len(result.data['contextArticles']['edges']) == 1 + assert result.data['contextArticles']['edges'][0]['node']['headline'] == 'a2' + + def test_filter_filterset_information_on_meta(): class ReporterFilterNode(DjangoObjectType):