question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Separating django-filter from relay connection

See original GitHub issue

Problem

Right now, the use of django-filter is tightly coupled with the use of the relay graphql format. I think it would be useful to separate the two so that filtering can be applied to a normal graphql list as well.

Solution

Pull out the django-filter logic from DjangoFilterConnectionField into something like DjangoFilterMixin. Then apply that mixin to DjangoFilterConnectionField and a new DjangoFilterField.

I’ve done something like this with success already. There’s a lot of copy-pasta though, and I think others could benefit from it. Here’s what we are currently using.

from graphene import Field
from graphene_django.filter.utils import (
    get_filtering_args_from_filterset,
    get_filterset_class
)

class DjangoFilterField(Field):

    def __init__(self, _type, fields=None, extra_filter_meta=None,
                 filterset_class=None, *args, **kwargs):

        _fields = _type._meta.filter_fields
        _model = _type._meta.model

        self.fields = fields or _fields
        meta = dict(model=_model, fields=self.fields)
        if extra_filter_meta:
            meta.update(extra_filter_meta)
        self.filterset_class = get_filterset_class(filterset_class, **meta)
        self.filtering_args = get_filtering_args_from_filterset(self.filterset_class, _type)
        kwargs.setdefault('args', {})
        kwargs['args'].update(self.filtering_args)
        super().__init__(List(_type), *args, **kwargs)

    @staticmethod
    def list_resolver(manager, filterset_class, filtering_args,
                      root, args, context, info):
        filter_kwargs = {k: v for k, v in args.items() if k in filtering_args}
        qs = manager.get_queryset()
        qs = filterset_class(data=filter_kwargs, queryset=qs).qs
        return qs

    def get_resolver(self, parent_resolver):
        return partial(self.list_resolver, self.type._meta.model._default_manager,
                       self.filterset_class, self.filtering_args)

@syrusakbary If this sounds like a good idea, I’d be happy to whip up the PR.

Issue Analytics

  • State:open
  • Created 6 years ago
  • Reactions:36
  • Comments:35 (5 by maintainers)

github_iconTop GitHub Comments

15reactions
zahir-koradiacommented, Dec 6, 2017

I haven’t tested this thoroughly, but the follow seems to work with graphene 2.0.

class DjangoFilterField(Field):
    '''
    Custom field to use django-filter with graphene object types (without relay).
    '''

    def __init__(self, _type, fields=None, extra_filter_meta=None,
                 filterset_class=None, *args, **kwargs):
        _fields = _type._meta.filter_fields
        _model = _type._meta.model
        self.of_type = _type
        self.fields = fields or _fields
        meta = dict(model=_model, fields=self.fields)
        if extra_filter_meta:
            meta.update(extra_filter_meta)
        self.filterset_class = get_filterset_class(filterset_class, **meta)
        self.filtering_args = get_filtering_args_from_filterset(
            self.filterset_class, _type)
        kwargs.setdefault('args', {})
        kwargs['args'].update(self.filtering_args)
        super().__init__(List(_type), *args, **kwargs)

    @staticmethod
    def list_resolver(manager, filterset_class, filtering_args, root, info, *args, **kwargs):
        filter_kwargs = {k: v for k,
                         v in kwargs.items() if k in filtering_args}
        qs = manager.get_queryset()
        qs = filterset_class(data=filter_kwargs, queryset=qs).qs
        return qs

    def get_resolver(self, parent_resolver):
        return partial(self.list_resolver, self.of_type._meta.model._default_manager,
                       self.filterset_class, self.filtering_args)

Key changes I have made are:

  1. Tracking type as self.type now returns List instead of the type of list it is.
  2. Changing params passed list_resolver.

I hope this is useful @Eraldo @eamigo86.

10reactions
jole78commented, Aug 17, 2017

I for one think it’s a great idea. I have for long wanted to use filtering on “normal” lists without the added overweight of forcing me to use connections, edges, nodes when I basically want to just filter a small list.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Graphene-Django nested filters (relay) - python - Stack Overflow
I came from django rest framework (DRF) and nested filtering works out of the box with django-filter , I only needed to specify...
Read more >
Relay - graphql-python
#1 : Relay allows you to use django-filter for filtering data. Here, you've defined a FilterSet, with the url and description ; #2...
Read more >
Relay tutorial - Graphene-Python
Testing everything so far¶. Update settings¶. Next, install your app and GraphiQL in your Django project. GraphiQL is a web-based integrated development ...
Read more >
field_name - django-filter - Read the Docs
An optional argument that tells the filter how to handle the queryset. It can accept either a callable or the name of a...
Read more >
How to build a GraphQL API using Python and Django
Once the virtual environment is activated, let's install Django, ... from graphene import ObjectType, relay from graphene_django.filter ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found