ObjectType
, Interface
, InputObjectType
, Scalar
and Enum
implementations
have been quite simplified, without the need to define a explicit Metaclass for each subtype.
It also improves the field resolvers, simplifying the code the developer has to write to use them.
Deprecations:
Breaking changes:
New Features!
InputObjectType
Meta as Class arguments
(only available for Python 3)
The type metaclasses are now deleted as they are no longer necessary. If your code was depending on this strategy for creating custom attrs, see an example on how to do it in 2.0.
AbstractType is deprecated in graphene 2.0, you can now use normal inheritance instead.
Before:
class CommonFields(AbstractType):
name = String()
class Pet(CommonFields, Interface):
pass
With 2.0:
class CommonFields(object):
name = String()
class Pet(CommonFields, Interface):
pass
resolve_only_args
is now deprecated as the resolver API has been simplified.
Before:
class User(ObjectType):
name = String()
@resolve_only_args
def resolve_name(self):
return self.name
With 2.0:
class User(ObjectType):
name = String()
def resolve_name(self, info):
return self.name
Mutation.Input
is now deprecated in favor of using Mutation.Arguments
(ClientIDMutation
still uses Input
).
Before:
class User(Mutation):
class Input:
name = String()
With 2.0:
class User(Mutation):
class Arguments:
name = String()
All the resolvers in graphene have been simplified.
Prior to Graphene 2.0
, all resolvers required four arguments: (root, args, context, info)
.
Now, resolver args
are passed as keyword arguments to the function, and context
argument dissapeared in favor of info.context
.
Before:
my_field = graphene.String(my_arg=graphene.String())
def resolve_my_field(self, args, context, info):
my_arg = args.get('my_arg')
return ...
With 2.0:
my_field = graphene.String(my_arg=graphene.String())
def resolve_my_field(self, info, my_arg):
return ...
PS.: Take care with receiving args like my_arg
as above. This doesn't work for optional (non-required) arguments as stantard Connection
's arguments (first, before, after, before).
You may need something like this:
def resolve_my_field(self, info, known_field1, known_field2, **args): ## get other args with: args.get('arg_key')
And, if you need the context in the resolver, you can use info.context
:
my_field = graphene.String(my_arg=graphene.String())
def resolve_my_field(self, info, my_arg):
context = info.context
return ...
Node types no longer have a Connection
by default.
In 2.0 and onwards Connection
s should be defined explicitly.
Before:
class User(ObjectType):
class Meta:
interfaces = [relay.Node]
name = String()
class Query(ObjectType):
user_connection = relay.ConnectionField(User)
With 2.0:
class User(ObjectType):
class Meta:
interfaces = [relay.Node]
name = String()
class UserConnection(relay.Connection):
class Meta:
node = User
class Query(ObjectType):
user_connection = relay.ConnectionField(UserConnection)
The method get_node
in ObjectTypes
that have Node
as interface, changes its API.
From def get_node(cls, id, context, info)
to def get_node(cls, info, id)
.
class MyObject(ObjectType):
class Meta:
interfaces = (Node, )
@classmethod
def get_node(cls, id, context, info):
return ...
To:
class MyObject(ObjectType):
class Meta:
interfaces = (Node, )
@classmethod
def get_node(cls, info, id):
return ...
The parameters' order of get_node_from_global_id
method has changed. You may need to adjust your Node Root Field and maybe other places that uses this method to obtain an object.
Before:
class RootQuery(object):
...
node = Field(relay.Node, id=ID(required=True))
def resolve_node(self, args, context, info):
node = relay.Node.get_node_from_global_id(args['id'], context, info)
return node
Now:
class RootQuery(object):
...
node = Field(relay.Node, id=ID(required=True))
def resolve_node(self, info, id):
node = relay.Node.get_node_from_global_id(info, id)
return node
Now only receives (self
, info
, **args
) and is not a @classmethod
Before:
class SomeMutation(Mutation):
...
@classmethod
def mutate(cls, instance, args, context, info):
...
With 2.0:
class SomeMutation(Mutation):
...
def mutate(self, info, **args):
...
With 2.0 you can also get your declared (as above) args
this way:
class SomeMutation(Mutation):
class Arguments:
first_name = String(required=True)
last_name = String(required=True)
...
def mutate(self, info, first_name, last_name):
...
Now only receives (root
, info
, **input
)
If you are using Middelwares, you need to some adjustments:
Before:
class MyGrapheneMiddleware(object):
def resolve(self, next_mw, root, args, context, info):
## Middleware code
return next_mw(root, args, context, info)
With 2.0:
class MyGrapheneMiddleware(object):
def resolve(self, next_mw, root, info, **args):
context = info.context
## Middleware code
info.context = context
return next_mw(root, info, **args)```
If you are using InputObjectType
, you now can access
its fields via getattr
(my_input.myattr
) when resolving, instead of
the classic way my_input['myattr']
.
And also use custom defined properties on your input class.
Example. Before:
class UserInput(InputObjectType):
id = ID(required=True)
def is_valid_input(input):
return input.get('id').startswith('userid_')
class Query(ObjectType):
user = graphene.Field(User, input=UserInput())
@resolve_only_args
def resolve_user(self, input):
user_id = input.get('id')
if is_valid_input(user_id):
return get_user(user_id)
With 2.0:
class UserInput(InputObjectType):
id = ID(required=True)
@property
def is_valid(self):
return self.id.startswith('userid_')
class Query(ObjectType):
user = graphene.Field(User, input=UserInput())
def resolve_user(self, info, input):
if input.is_valid:
return get_user(input.id)
Now you can use the meta options as class arguments (ONLY PYTHON 3).
Before:
class Dog(ObjectType):
class Meta:
interfaces = [Pet]
name = String()
With 2.0:
class Dog(ObjectType, interfaces=[Pet]):
name = String()
Now you can create abstact types super easily, without the need of subclassing the meta.
class Base(ObjectType):
class Meta:
abstract = True
id = ID()
def resolve_id(self, info):
return "{type}_{id}".format(
type=self.__class__.__name__,
id=self.id
)
In Graphene 2.0 there is a new dedicated scalar for UUIDs, UUID
.