2017-07-12 09:52:24 +03:00
# v2.0 Upgrade Guide
2017-07-12 06:33:03 +03:00
2017-07-24 06:12:54 +03:00
`ObjectType` , `Interface` , `InputObjectType` , `Scalar` and `Enum` implementations
have been quite simplified, without the need to define a explicit Metaclass for each subtype.
2017-07-27 10:10:22 +03:00
It also improves the field resolvers, [simplifying the code ](#simpler-resolvers ) the
2017-08-03 22:20:11 +03:00
developer has to write to use them.
2017-07-24 06:12:54 +03:00
2017-07-27 10:13:12 +03:00
**Deprecations:**
2019-06-10 02:49:56 +03:00
- [`AbstractType` ](#abstracttype-deprecated )
- [`resolve_only_args` ](#resolve_only_args )
- [`Mutation.Input` ](#mutationinput )
2017-07-24 06:12:54 +03:00
2017-07-27 10:13:12 +03:00
**Breaking changes:**
2019-06-10 02:49:56 +03:00
- [`Simpler Resolvers` ](#simpler-resolvers )
- [`Node Connections` ](#node-connections )
2017-07-24 06:12:54 +03:00
2017-07-27 10:13:12 +03:00
**New Features!**
2017-07-24 06:12:54 +03:00
2019-06-10 02:49:56 +03:00
- [`InputObjectType` ](#inputobjecttype )
- [`Meta as Class arguments` ](#meta-as-class-arguments ) (_only available for Python 3_)
2017-07-24 06:12:54 +03:00
2019-06-10 02:49:56 +03:00
> The type metaclasses are now deleted as they are no longer necessary. If your code was depending
2018-02-05 17:49:16 +03:00
> on this strategy for creating custom attrs, see an [example on how to do it in 2.0](https://github.com/graphql-python/graphene/blob/v2.0.0/graphene/tests/issues/test_425.py).
2017-07-12 09:07:20 +03:00
2017-07-12 06:33:03 +03:00
## Deprecations
2017-07-24 06:12:54 +03:00
### AbstractType deprecated
2017-07-12 06:33:03 +03:00
2017-07-24 06:12:54 +03:00
AbstractType is deprecated in graphene 2.0, you can now use normal inheritance instead.
2017-07-12 06:33:03 +03:00
2017-07-24 06:12:54 +03:00
Before:
2017-07-12 06:33:03 +03:00
2017-07-24 06:12:54 +03:00
```python
class CommonFields(AbstractType):
name = String()
2017-07-12 06:33:03 +03:00
2017-07-24 06:12:54 +03:00
class Pet(CommonFields, Interface):
pass
```
2017-07-12 08:52:38 +03:00
2017-07-24 06:12:54 +03:00
With 2.0:
2017-07-12 08:52:38 +03:00
2017-07-24 06:12:54 +03:00
```python
class CommonFields(object):
name = String()
2017-07-12 08:52:38 +03:00
2017-07-24 06:12:54 +03:00
class Pet(CommonFields, Interface):
pass
```
2019-06-10 02:49:56 +03:00
### resolve_only_args
2017-07-12 08:52:38 +03:00
2017-07-27 13:00:21 +03:00
`resolve_only_args` is now deprecated as the resolver API has been simplified.
2017-07-24 06:12:54 +03:00
Before:
```python
class User(ObjectType):
name = String()
@resolve_only_args
2019-06-10 02:49:56 +03:00
def resolve_name(root):
return root.name
2017-07-24 06:12:54 +03:00
```
With 2.0:
```python
class User(ObjectType):
name = String()
2019-06-10 02:49:56 +03:00
def resolve_name(root, info):
return root.name
2017-07-24 06:12:54 +03:00
```
2017-07-12 08:52:38 +03:00
2017-07-27 05:44:17 +03:00
### Mutation.Input
2017-08-03 22:20:11 +03:00
`Mutation.Input` is now deprecated in favor of using `Mutation.Arguments` (`ClientIDMutation` still uses `Input` ).
2017-07-27 05:44:17 +03:00
Before:
```python
class User(Mutation):
class Input:
name = String()
```
With 2.0:
```python
class User(Mutation):
class Arguments:
name = String()
```
2017-07-13 07:53:35 +03:00
## Breaking Changes
2017-07-27 10:13:12 +03:00
### Simpler resolvers
2017-08-07 22:28:18 +03:00
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` .
2017-07-27 10:13:12 +03:00
Before:
```python
my_field = graphene.String(my_arg=graphene.String())
2019-06-10 02:49:56 +03:00
def resolve_my_field(root, args, context, info):
2017-07-27 10:13:12 +03:00
my_arg = args.get('my_arg')
return ...
```
With 2.0:
```python
my_field = graphene.String(my_arg=graphene.String())
2019-06-10 02:49:56 +03:00
def resolve_my_field(root, info, my_arg):
2017-07-27 10:13:12 +03:00
return ...
```
2022-07-16 07:40:00 +03:00
**PS.: Take care with receiving args like `my_arg` as above. This doesn't work for optional (non-required) arguments as standard `Connection` 's arguments (first, last, after, before).**
2018-05-24 02:13:03 +03:00
You may need something like this:
```python
2019-06-10 02:49:56 +03:00
def resolve_my_field(root, info, known_field1, known_field2, **args): ## get other args with: args.get('arg_key')
2018-05-24 02:13:03 +03:00
```
2017-08-07 22:28:18 +03:00
And, if you need the context in the resolver, you can use `info.context` :
2017-07-27 10:13:12 +03:00
```python
my_field = graphene.String(my_arg=graphene.String())
2019-06-10 02:49:56 +03:00
def resolve_my_field(root, info, my_arg):
2017-08-07 22:28:18 +03:00
context = info.context
2017-07-27 10:13:12 +03:00
return ...
```
2017-07-24 06:12:54 +03:00
### Node Connections
Node types no longer have a `Connection` by default.
In 2.0 and onwards `Connection` s should be defined explicitly.
Before:
```python
class User(ObjectType):
class Meta:
interfaces = [relay.Node]
name = String()
2017-07-24 09:16:51 +03:00
2017-07-24 06:12:54 +03:00
class Query(ObjectType):
user_connection = relay.ConnectionField(User)
```
With 2.0:
```python
class User(ObjectType):
class Meta:
interfaces = [relay.Node]
name = String()
class UserConnection(relay.Connection):
class Meta:
node = User
2017-07-13 07:53:35 +03:00
2017-07-24 06:12:54 +03:00
class Query(ObjectType):
user_connection = relay.ConnectionField(UserConnection)
```
2017-07-24 04:57:17 +03:00
2017-07-28 06:06:48 +03:00
## Node.get_node
2017-08-03 22:20:11 +03:00
The method `get_node` in `ObjectTypes` that have `Node` as interface, changes its API.
2017-07-28 06:06:48 +03:00
From `def get_node(cls, id, context, info)` to `def get_node(cls, info, id)` .
```python
class MyObject(ObjectType):
class Meta:
interfaces = (Node, )
@classmethod
def get_node(cls, id, context, info):
return ...
```
To:
2019-06-10 02:49:56 +03:00
2017-07-28 06:06:48 +03:00
```python
class MyObject(ObjectType):
class Meta:
interfaces = (Node, )
@classmethod
def get_node(cls, info, id):
return ...
```
2017-07-27 12:51:25 +03:00
2018-06-01 23:15:34 +03:00
## Node.get_node_from_global_id
The parameters' order of `get_node_from_global_id` method has changed. You may need to adjust your [Node Root Field ](http://docs.graphene-python.org/en/latest/relay/nodes/#node-root-field ) and maybe other places that uses this method to obtain an object.
Before:
2019-06-10 02:49:56 +03:00
2018-06-01 23:15:34 +03:00
```python
class RootQuery(object):
...
node = Field(relay.Node, id=ID(required=True))
2019-06-10 02:49:56 +03:00
def resolve_node(root, args, context, info):
2018-06-01 23:15:34 +03:00
node = relay.Node.get_node_from_global_id(args['id'], context, info)
return node
```
Now:
2019-06-10 02:49:56 +03:00
2018-06-01 23:15:34 +03:00
```python
class RootQuery(object):
...
node = Field(relay.Node, id=ID(required=True))
2019-06-10 02:49:56 +03:00
def resolve_node(root, info, id):
2018-06-01 23:15:34 +03:00
node = relay.Node.get_node_from_global_id(info, id)
return node
```
2017-07-27 12:51:25 +03:00
## Mutation.mutate
2019-06-10 02:49:56 +03:00
Now only receives (`root`, `info` , `**kwargs` ) and is not a @classmethod
2018-05-24 02:13:03 +03:00
Before:
```python
class SomeMutation(Mutation):
...
2018-05-28 21:18:54 +03:00
2018-05-24 02:13:03 +03:00
@classmethod
def mutate(cls, instance, args, context, info):
...
```
With 2.0:
```python
class SomeMutation(Mutation):
...
2018-05-28 21:18:54 +03:00
2019-06-10 02:49:56 +03:00
def mutate(root, info, **args):
2018-05-24 02:13:03 +03:00
...
```
With 2.0 you can also get your declared (as above) `args` this way:
```python
class SomeMutation(Mutation):
class Arguments:
first_name = String(required=True)
last_name = String(required=True)
...
2018-05-28 21:18:54 +03:00
2019-06-10 02:49:56 +03:00
def mutate(root, info, first_name, last_name):
2018-05-24 02:13:03 +03:00
...
```
2017-07-27 12:51:25 +03:00
## ClientIDMutation.mutate_and_get_payload
2017-07-28 06:06:48 +03:00
Now only receives (`root`, `info` , `**input` )
2017-07-27 12:51:25 +03:00
2018-05-24 02:13:03 +03:00
### Middlewares
If you are using Middelwares, you need to some adjustments:
Before:
```python
2019-03-08 22:09:45 +03:00
class MyGrapheneMiddleware(object):
2018-05-24 02:13:03 +03:00
def resolve(self, next_mw, root, args, context, info):
## Middleware code
return next_mw(root, args, context, info)
```
With 2.0:
```python
2019-03-08 22:09:45 +03:00
class MyGrapheneMiddleware(object):
2018-05-24 02:13:03 +03:00
def resolve(self, next_mw, root, info, **args):
context = info.context
## Middleware code
info.context = context
2019-06-10 02:49:56 +03:00
return next_mw(root, info, **args)
2018-05-24 02:13:03 +03:00
```
2017-07-24 04:57:17 +03:00
## New Features
### InputObjectType
2017-07-24 06:12:54 +03:00
If you are using `InputObjectType` , you now can access
2017-08-03 22:20:11 +03:00
its fields via `getattr` (`my_input.myattr`) when resolving, instead of
2017-07-24 04:57:17 +03:00
the classic way `my_input['myattr']` .
And also use custom defined properties on your input class.
Example. Before:
```python
class UserInput(InputObjectType):
2017-07-27 06:12:15 +03:00
id = ID(required=True)
2017-07-24 04:57:17 +03:00
2017-07-27 06:12:15 +03:00
def is_valid_input(input):
return input.get('id').startswith('userid_')
2017-07-24 04:57:17 +03:00
class Query(ObjectType):
2017-07-24 09:10:15 +03:00
user = graphene.Field(User, input=UserInput())
2017-07-24 04:57:17 +03:00
@resolve_only_args
2019-06-10 02:49:56 +03:00
def resolve_user(root, input):
2017-07-24 04:57:17 +03:00
user_id = input.get('id')
2017-07-27 06:12:15 +03:00
if is_valid_input(user_id):
2017-07-24 04:57:17 +03:00
return get_user(user_id)
```
With 2.0:
```python
class UserInput(InputObjectType):
2017-07-27 06:12:15 +03:00
id = ID(required=True)
2017-07-24 04:57:17 +03:00
@property
2019-06-10 02:49:56 +03:00
def is_valid(root):
return root.id.startswith('userid_')
2017-07-24 04:57:17 +03:00
class Query(ObjectType):
2017-07-24 09:10:15 +03:00
user = graphene.Field(User, input=UserInput())
2017-07-24 04:57:17 +03:00
2019-06-10 02:49:56 +03:00
def resolve_user(root, info, input):
2017-07-27 06:12:15 +03:00
if input.is_valid:
2017-07-24 04:57:17 +03:00
return get_user(input.id)
2017-07-24 06:12:54 +03:00
```
### Meta as Class arguments
Now you can use the meta options as class arguments (**ONLY PYTHON 3**).
2017-07-24 04:57:17 +03:00
2017-07-24 06:12:54 +03:00
Before:
```python
class Dog(ObjectType):
class Meta:
interfaces = [Pet]
name = String()
```
With 2.0:
```python
class Dog(ObjectType, interfaces=[Pet]):
name = String()
2017-07-24 04:57:17 +03:00
```
2017-07-25 09:15:47 +03:00
2017-07-27 05:44:17 +03:00
### Abstract types
Now you can create abstact types super easily, without the need of subclassing the meta.
```python
class Base(ObjectType):
class Meta:
abstract = True
2018-05-22 20:56:58 +03:00
2017-07-27 05:44:17 +03:00
id = ID()
2019-06-10 02:49:56 +03:00
def resolve_id(root, info):
2020-03-15 03:32:44 +03:00
return f"{root.__class__.__name__}_{root.id}"
2017-07-27 05:44:17 +03:00
```
2017-07-25 09:15:47 +03:00
### UUID Scalar
In Graphene 2.0 there is a new dedicated scalar for UUIDs, `UUID` .