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.

Ability to have one class per query instead of one method per query

See original GitHub issue
[x] Feature request
[x] Or - Docs request
graphene==2.1.0

Is it possible to have a similar pattern for queries, similar to mutation. This is to avoid long classes with multiple resolvers.

It would be great if this would be possible.

# mutation -- working example
class JwtRefreshMutation(graphene.Mutation):
    class Arguments:
        token=graphene.String(required=True)

    token = graphene.String()

    def mutate(self, info, **args):
        token = refresh_token(args['token'])
        return JwtRefreshMutation(token=token)

class Mutations(graphene.ObjectType):
    jwt_refresh = JwtRefreshMutation.Field()
# query -- desired example
class JwtRefreshQuery(graphene.Query):
    class Arguments:
        token=graphene.String(required=True)

    token = graphene.String()

    def resolve(self, info, **args):
        token = refresh_token(args['token'])
        return JwtRefreshQuery(token=token)

class Query(graphene.ObjectType):
    jwt_refresh = JwtRefreshQuery.Field()

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:8 (3 by maintainers)

github_iconTop GitHub Comments

8reactions
jkimbocommented, May 6, 2018

So I’ve been thinking about this a lot and I think that being able to define a class to resolve an ObjectType would be quite useful. I’ve got some working code based on the Mutation class @un33k if you want to include it in your application and see if it works:

from collections import OrderedDict

from graphene.utils.get_unbound_function import get_unbound_function
from graphene.utils.props import props
from graphene.types.field import Field
from graphene.types.objecttype import ObjectType, ObjectTypeOptions
from graphene.types.utils import yank_fields_from_attrs
from graphene.utils.deprecated import warn_deprecation


class FieldResolverOptions(ObjectTypeOptions):
    arguments = None  # type: Dict[str, Argument]
    output = None  # type: Type[ObjectType]
    resolver = None  # type: Callable


class FieldResolver(ObjectType):
    @classmethod
    def __init_subclass_with_meta__(cls, resolver=None, output=None, arguments=None,
                                    _meta=None, **options):
        if not _meta:
            _meta = FieldResolverOptions(cls)

        output = output or getattr(cls, 'Output', None)
        fields = {}
        if not output:
            # If output is defined, we don't need to get the fields
            fields = OrderedDict()
            for base in reversed(cls.__mro__):
                fields.update(
                    yank_fields_from_attrs(base.__dict__, _as=Field)
                )
            output = cls

        if not arguments:
            input_class = getattr(cls, 'Arguments', None)
            if input_class:
                arguments = props(input_class)
            else:
                arguments = {}

        if not resolver:
            _resolver = getattr(cls, 'resolve', None)
            assert _resolver, 'All field resolvers must define a resolve method'
            resolver = get_unbound_function(_resolver)

        if _meta.fields:
            _meta.fields.update(fields)
        else:
            _meta.fields = fields

        _meta.output = output
        _meta.resolver = resolver
        _meta.arguments = arguments

        super(FieldResolver, cls).__init_subclass_with_meta__(
            _meta=_meta, **options)

    @classmethod
    def Field(cls, name=None, description=None, deprecation_reason=None, required=False):
        return Field(
            cls._meta.output,
            args=cls._meta.arguments,
            resolver=cls._meta.resolver,
            name=name,
            description=description,
            deprecation_reason=deprecation_reason,
            required=required,
        )

You use it in pretty much the way you want to in your example:

class JwtRefreshQuery(FieldResolver):
    class Arguments:
        token = graphene.String(required=True)

    token = graphene.String()

    def resolve(self, info, **args):
        token = refresh_token(args['token'])
        return JwtRefreshQuery(token=token)

class Query(graphene.ObjectType):
    jwt_refresh = JwtRefreshQuery.Field()

I’m not sure what to call it at the moment or where it might fit in in the project. It might even be that the ObjectType class should be extended to allow this kind of use. Any thoughts @syrusakbary ?

5reactions
HeyHugocommented, May 1, 2018

Hi,

you can do like this:

class JwtRefreshQuery(object):
    token = graphene.String(graphene.String, token=graphene.String(required=True))

    def resolve_token(self, info, token):
        return refresh_token(token)


class Query(JwtRefreshQuery, SomeOtherQuery, graphene.ObjectType):
    pass

I agree intuitively I think it would make more sense if the API for mutations and queries were more similar. In any case yes the docs/examles are a bit lacking in showing how you can do “merge multiple query classes”

Read more comments on GitHub >

github_iconTop Results From Across the Web

Best way to structure multiple queries in one method c# asp.net
IMO, the best way is to use one connection for one query. You want to execute another query? Set up another connection. :)...
Read more >
Defining and Using Class Queries
Describes how to define and use class queries, named queries that are defined in a class and can be accessed using dynamic SQL....
Read more >
Use a union query to combine multiple queries into a single ...
The second part of this SQL statement is the UNION keyword which tells Access that this query will combine these two sets of...
Read more >
Best practices for querying and scanning data
Follow these best practices for Query and Scan operations using DynamoDB. ... We recommend that you begin with a simple ratio, such as...
Read more >
Creating database connections - Do it once or for each query?
-1 The answer is rather misleading. Creating a connection per query is a very bad idea. What you probably mean is "retrieve a...
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