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.

Adding Promise support for DjangoConnectionField

See original GitHub issue

Now that Graphene 1.4 and Promise 2.0 are out, I am now using them on my Django application, particularly promise.DataLoader, and I came across the fact that the DjangoConnectionField.connection_resolver method is not compatible with Promises, but fortunately, solving that is trivial, thanks to some tactical copypasting:

from functools import partial
from promise import Promise
from django.db.models import QuerySet, Manager
from graphene_django.fields import DjangoConnectionField
from graphene.relay import PageInfo
from graphql_relay.connection.arrayconnection import connection_from_list_slice


class PromiseDjangoConnectionField(DjangoConnectionField):
    @classmethod
    def connection_from_iterable(cls, args, connection, default_manager, iterable):
        if iterable is None:
            iterable = default_manager

        if isinstance(iterable, Manager):
            iterable = iterable.get_queryset()

        if isinstance(iterable, QuerySet):
            if iterable is not default_manager:
                default_queryset = default_manager.get_queryset()
                iterable = default_queryset & iterable
            _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

    @classmethod
    def connection_resolver(cls, resolver, connection, default_manager, max_limit,
                            enforce_first_or_last, root, args, context, info):
        first = args.get('first')
        last = args.get('last')

        if enforce_first_or_last:
            assert first or last, (
                'You must provide a `first` or `last` value to properly paginate the `{}` connection.'
            ).format(info.field_name)

        if max_limit:
            if first:
                assert first <= max_limit, (
                    'Requesting {} records on the `{}` connection exceeds the `first` limit of {} records.'
                ).format(first, info.field_name, max_limit)
                args['first'] = min(first, max_limit)

            if last:
                assert last <= max_limit, (
                    'Requesting {} records on the `{}` connection exceeds the `last` limit of {} records.'
                ).format(first, info.field_name, max_limit)
                args['last'] = min(last, max_limit)
    
        iterable = resolver(root, args, context, info)
        connection_from_iterable = partial(
            cls.connection_from_iterable,
            args,
            connection,
            default_manager
        )

        if Promise.is_thenable(iterable):
            return iterable.then(connection_from_iterable)
        else:
            return connection_from_iterable(iterable)

If @syrusakbary gives me the green light, I’ll write a PR with a few tests. 😄

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:1
  • Comments:20 (1 by maintainers)

github_iconTop GitHub Comments

5reactions
japrogramercommented, Jun 21, 2019

This won’t solve n+1, no where in computer science can n+1 be solved only alleviated. not even quantum computers will solve n+1;

DataLoaders will help if for some reason you keep requesting the same item multiple times per query. But usually it’s nested queries that causes the exponential growth … so prefetching and select_related is your best option. But there is an open bug on how graphene-django throws away prefetched data … so …

3reactions
ngshihengcommented, Dec 28, 2020

@here or @Xzya Did you manage to get it working with DjangoFilterConnectionField?

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to combine DjangoObjectType and an additional field in ...
I need to add another field that is not in ExampleType to the query ... However, I'm getting many errors such as DjangoConnectionField...
Read more >
Django + GraphQL. Solving N+1 Problem using DataLoaders
from graphene_django import DjangoConnectionField, DjangoObjectType ... of keys and returns a Promise which resolves to a list of values:.
Read more >
Solving N+1 in GraphQL Python With Dataloader - Jerry Ng
Collects a list of keys (object IDs); Calls a batch loading function with the list of keys; Returns a Promise which resolves to...
Read more >
graphene Changelog - pyup.io
Dependency on unused promise Library was removed: 1476 by mike-roberts-healx - Docs improvements by rgroothuijsen. All Changes * feat: Add support for ...
Read more >
How to use the graphene-django.graphene_django.fields ...
To help you get started, we've selected a few graphene-django.graphene_django.fields.DjangoConnectionField examples, based on popular ways it is used in ...
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