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.

DjangoFilterConnectionField and resolve support

See original GitHub issue

I found that the following example from the docs doesn’t work correctly.

The thing is that if there is a resolver for a DjangoFilterConnectionField like in the example, the filtering and ordering created by DjangoFilterConnectionField will be ignored. Details below.

class Query(ObjectType):
    my_posts = DjangoFilterConnectionField(CategoryNode)

    def resolve_my_posts(self, args, context, info):
        # context will reference to the Django request
        if not context.user.is_authenticated():
            return Post.objects.none()
        else:
            return Post.objects.filter(owner=context.user)

The issues seems to be that DjangoFilterConnectionField ignores what the resolver returns.

@staticmethod
def connection_resolver(resolver, connection, default_manager, filterset_class, filtering_args,
                        root, args, context, info):
    filter_kwargs = {k: v for k, v in args.items() if k in filtering_args}
    order = args.get('order_by', None)
    qs = default_manager.get_queryset()
    if order:
        qs = qs.order_by(order)
    qs = filterset_class(data=filter_kwargs, queryset=qs)

    return DjangoConnectionField.connection_resolver(resolver, connection, qs, root, args, context, info)

Here the QuerySet is built just fine but the resolver does not have an impact on the QuerySet (which is fine if no resolver is used anyway).

The problem arrises when the DjangoConnectionField resolves the query.

@staticmethod
def connection_resolver(resolver, connection, default_manager, root, args, context, info):
    iterable = resolver(root, args, context, info)
    if iterable is None:
        iterable = default_manager
    iterable = maybe_queryset(iterable)
    if isinstance(iterable, QuerySet):
        _len = iterable.count()
    else:
        _len = len(iterable)
    connection = connection_from_list_slice(
        iterable,
        args,
        slice_start=0,
        list_length=_len,
        list_slice_length=_len,
        connection_type=connection,
        edge_type=connection.Edge,
        pageinfo_type=PageInfo,
    )
    connection.iterable = iterable
    connection.length = _len
    return connection

Here we consider the default_manager (which is the QuerySet that was built by DjangoFilterConnectionField) only if there resolver is None.

So you either can have the DjangoFilterConnection field or a resolver. But it should be possible to use both (to limit an initial QuerySet and check of authorisation, etc.).

Solution

The solution I use now locally by changing DjangoFilterConnectionField is the following:

@staticmethod
def connection_resolver(resolver, connection, default_manager, filterset_class, filtering_args,
                        root, args, context, info):
    filter_kwargs = {k: v for k, v in args.items() if k in filtering_args}
    order = args.get('order_by', None)

    def new_resolver(root, args, context, info):
        qs = resolver(root, args, context, info)
        if qs is None:
            qs = default_manager.get_queryset()
        if order:
            qs = qs.order_by(order)
        qs = filterset_class(data=filter_kwargs, queryset=qs)
        return qs

    return DjangoConnectionField.connection_resolver(new_resolver, connection, None, root, args, context, info)

Basically it wraps the resolver and uses its QuerySet if available or create one with the default_manager. It also guarantees that the resolver returns something and therefore we can set the default_manager to None just to make sure it there is no other way to get to the QuerySet.

I would have created a PR but I couldn’t get the tests of the repo running locally. 😦

PS: I’d also really like to get the tests running so I can contribute to the code directly. Any pointers how to get it running are really appreciated as I didn’t have luck by following the instructions in “Contributing”.

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:7
  • Comments:18 (6 by maintainers)

github_iconTop GitHub Comments

3reactions
michalochmancommented, Oct 26, 2016

The above solution also fixes #21

1reaction
drakoncommented, Dec 26, 2016

@ayozemr I haven’t got the tests running (haven’t tried it since November though.).

My recommended solution would be to just use a custom class for this and override the function like I did above.

class MyDjangoFilterConnectionField(DjangoFilterConnectionField):
    """
    Workaround to make resolver work with DjangoFilterConnectionField

    See also: https://github.com/graphql-python/graphene-django/issues/30
    """
    @staticmethod
    def connection_resolver(resolver, connection, default_manager, filterset_class, filtering_args,
                            root, args, context, info):
        filter_kwargs = {k: v for k, v in args.items() if k in filtering_args}
        order = args.get('order_by', None)

        def new_resolver(root, args, context, info):
            qs = resolver(root, args, context, info)
            if qs is None:
                qs = default_manager.get_queryset()
            if order:
                qs = qs.order_by(order)
            qs = filterset_class(data=filter_kwargs, queryset=qs)
            return qs

        return DjangoConnectionField.connection_resolver(new_resolver, connection, None, root, args, context, info)

I now use this class instead of the DjangoFilterConnectionField. Not the prettiest solution but until the issue is fixed this works.

If someone gets the tests running, I’m happy to create a PR with the fix.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Developers - DjangoFilterConnectionField and resolve support -
DjangoFilterConnectionField and resolve support ... I found that the following example from the docs doesn't work correctly. The thing is that if ...
Read more >
Using DjangoFilterConnectionField with custom Connection in ...
From my understanding, User node needs to be passed UserConnection on it's meta, and UserConnection needs to be passed User on it's meta....
Read more >
How to use the graphene.relay.Node.Field function in ... - Snyk
To help you get started, we've selected a few graphene.relay. ... DjangoFilterConnectionField(BookNode) def resolve_users(self): return User.objects.all() ...
Read more >
django-filter - Graphene-Python
Field(AnimalNode) all_animals = DjangoFilterConnectionField(AnimalNode) ... Node,) def resolve_users(self, info, **kwargs): return UserFilter(kwargs).qs.
Read more >
Chapter 4. Graphene - Michael Stromer
As it turns out, there is already support for GraphQL and Python using the ... import DjangoFilterConnectionField from catalog.filters import BookFilter ...
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