mirror of
				https://github.com/graphql-python/graphene.git
				synced 2025-11-04 01:47:45 +03:00 
			
		
		
		
	There are small typos in: - UPGRADE-v1.0.md - UPGRADE-v2.0.md - docs/execution/fileuploading.rst Fixes: - Should read `standard` rather than `stantard`. - Should read `library` rather than `libary`. - Should read `explicitly` rather than `explicity`. Signed-off-by: Tim Gates <tim.gates@iress.com>
		
			
				
	
	
		
			386 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			386 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
# v2.0 Upgrade Guide
 | 
						||
 | 
						||
`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](#simpler-resolvers) the
 | 
						||
developer has to write to use them.
 | 
						||
 | 
						||
**Deprecations:**
 | 
						||
 | 
						||
- [`AbstractType`](#abstracttype-deprecated)
 | 
						||
- [`resolve_only_args`](#resolve_only_args)
 | 
						||
- [`Mutation.Input`](#mutationinput)
 | 
						||
 | 
						||
**Breaking changes:**
 | 
						||
 | 
						||
- [`Simpler Resolvers`](#simpler-resolvers)
 | 
						||
- [`Node Connections`](#node-connections)
 | 
						||
 | 
						||
**New Features!**
 | 
						||
 | 
						||
- [`InputObjectType`](#inputobjecttype)
 | 
						||
- [`Meta as Class arguments`](#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](https://github.com/graphql-python/graphene/blob/v2.0.0/graphene/tests/issues/test_425.py).
 | 
						||
 | 
						||
## Deprecations
 | 
						||
 | 
						||
### AbstractType deprecated
 | 
						||
 | 
						||
AbstractType is deprecated in graphene 2.0, you can now use normal inheritance instead.
 | 
						||
 | 
						||
Before:
 | 
						||
 | 
						||
```python
 | 
						||
class CommonFields(AbstractType):
 | 
						||
    name = String()
 | 
						||
 | 
						||
class Pet(CommonFields, Interface):
 | 
						||
    pass
 | 
						||
```
 | 
						||
 | 
						||
With 2.0:
 | 
						||
 | 
						||
```python
 | 
						||
class CommonFields(object):
 | 
						||
    name = String()
 | 
						||
 | 
						||
class Pet(CommonFields, Interface):
 | 
						||
    pass
 | 
						||
```
 | 
						||
 | 
						||
### resolve_only_args
 | 
						||
 | 
						||
`resolve_only_args` is now deprecated as the resolver API has been simplified.
 | 
						||
 | 
						||
Before:
 | 
						||
 | 
						||
```python
 | 
						||
class User(ObjectType):
 | 
						||
    name = String()
 | 
						||
 | 
						||
    @resolve_only_args
 | 
						||
    def resolve_name(root):
 | 
						||
        return root.name
 | 
						||
```
 | 
						||
 | 
						||
With 2.0:
 | 
						||
 | 
						||
```python
 | 
						||
class User(ObjectType):
 | 
						||
    name = String()
 | 
						||
 | 
						||
    def resolve_name(root, info):
 | 
						||
        return root.name
 | 
						||
```
 | 
						||
 | 
						||
### Mutation.Input
 | 
						||
 | 
						||
`Mutation.Input` is now deprecated in favor of using `Mutation.Arguments` (`ClientIDMutation` still uses `Input`).
 | 
						||
 | 
						||
Before:
 | 
						||
 | 
						||
```python
 | 
						||
class User(Mutation):
 | 
						||
    class Input:
 | 
						||
        name = String()
 | 
						||
```
 | 
						||
 | 
						||
With 2.0:
 | 
						||
 | 
						||
```python
 | 
						||
class User(Mutation):
 | 
						||
    class Arguments:
 | 
						||
        name = String()
 | 
						||
```
 | 
						||
 | 
						||
## Breaking Changes
 | 
						||
 | 
						||
### Simpler resolvers
 | 
						||
 | 
						||
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:
 | 
						||
 | 
						||
```python
 | 
						||
my_field = graphene.String(my_arg=graphene.String())
 | 
						||
 | 
						||
def resolve_my_field(root, args, context, info):
 | 
						||
    my_arg = args.get('my_arg')
 | 
						||
    return ...
 | 
						||
```
 | 
						||
 | 
						||
With 2.0:
 | 
						||
 | 
						||
```python
 | 
						||
my_field = graphene.String(my_arg=graphene.String())
 | 
						||
 | 
						||
def resolve_my_field(root, 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 standard `Connection`'s arguments (first, last, after, before).**
 | 
						||
You may need something like this:
 | 
						||
 | 
						||
```python
 | 
						||
def resolve_my_field(root, 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`:
 | 
						||
 | 
						||
```python
 | 
						||
my_field = graphene.String(my_arg=graphene.String())
 | 
						||
 | 
						||
def resolve_my_field(root, info, my_arg):
 | 
						||
    context = info.context
 | 
						||
    return ...
 | 
						||
```
 | 
						||
 | 
						||
### 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()
 | 
						||
 | 
						||
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
 | 
						||
 | 
						||
class Query(ObjectType):
 | 
						||
    user_connection = relay.ConnectionField(UserConnection)
 | 
						||
```
 | 
						||
 | 
						||
## Node.get_node
 | 
						||
 | 
						||
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)`.
 | 
						||
 | 
						||
```python
 | 
						||
class MyObject(ObjectType):
 | 
						||
    class Meta:
 | 
						||
        interfaces = (Node, )
 | 
						||
 | 
						||
    @classmethod
 | 
						||
    def get_node(cls, id, context, info):
 | 
						||
        return ...
 | 
						||
```
 | 
						||
 | 
						||
To:
 | 
						||
 | 
						||
```python
 | 
						||
class MyObject(ObjectType):
 | 
						||
    class Meta:
 | 
						||
        interfaces = (Node, )
 | 
						||
 | 
						||
    @classmethod
 | 
						||
    def get_node(cls, info, id):
 | 
						||
        return ...
 | 
						||
```
 | 
						||
 | 
						||
## 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:
 | 
						||
 | 
						||
```python
 | 
						||
class RootQuery(object):
 | 
						||
    ...
 | 
						||
    node = Field(relay.Node, id=ID(required=True))
 | 
						||
 | 
						||
    def resolve_node(root, args, context, info):
 | 
						||
        node = relay.Node.get_node_from_global_id(args['id'], context, info)
 | 
						||
        return node
 | 
						||
```
 | 
						||
 | 
						||
Now:
 | 
						||
 | 
						||
```python
 | 
						||
class RootQuery(object):
 | 
						||
    ...
 | 
						||
    node = Field(relay.Node, id=ID(required=True))
 | 
						||
 | 
						||
    def resolve_node(root, info, id):
 | 
						||
        node = relay.Node.get_node_from_global_id(info, id)
 | 
						||
        return node
 | 
						||
```
 | 
						||
 | 
						||
## Mutation.mutate
 | 
						||
 | 
						||
Now only receives (`root`, `info`, `**kwargs`) and is not a @classmethod
 | 
						||
 | 
						||
Before:
 | 
						||
 | 
						||
```python
 | 
						||
class SomeMutation(Mutation):
 | 
						||
    ...
 | 
						||
 | 
						||
    @classmethod
 | 
						||
    def mutate(cls, instance, args, context, info):
 | 
						||
        ...
 | 
						||
```
 | 
						||
 | 
						||
With 2.0:
 | 
						||
 | 
						||
```python
 | 
						||
class SomeMutation(Mutation):
 | 
						||
    ...
 | 
						||
 | 
						||
    def mutate(root, info, **args):
 | 
						||
        ...
 | 
						||
```
 | 
						||
 | 
						||
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)
 | 
						||
    ...
 | 
						||
 | 
						||
    def mutate(root, info, first_name, last_name):
 | 
						||
        ...
 | 
						||
```
 | 
						||
 | 
						||
## ClientIDMutation.mutate_and_get_payload
 | 
						||
 | 
						||
Now only receives (`root`, `info`, `**input`)
 | 
						||
 | 
						||
### Middlewares
 | 
						||
 | 
						||
If you are using Middelwares, you need to some adjustments:
 | 
						||
 | 
						||
Before:
 | 
						||
 | 
						||
```python
 | 
						||
class MyGrapheneMiddleware(object):
 | 
						||
    def resolve(self, next_mw, root, args, context, info):
 | 
						||
 | 
						||
        ## Middleware code
 | 
						||
 | 
						||
        return next_mw(root, args, context, info)
 | 
						||
```
 | 
						||
 | 
						||
With 2.0:
 | 
						||
 | 
						||
```python
 | 
						||
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)
 | 
						||
```
 | 
						||
 | 
						||
## New Features
 | 
						||
 | 
						||
### InputObjectType
 | 
						||
 | 
						||
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:
 | 
						||
 | 
						||
```python
 | 
						||
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(root, input):
 | 
						||
        user_id = input.get('id')
 | 
						||
        if is_valid_input(user_id):
 | 
						||
            return get_user(user_id)
 | 
						||
```
 | 
						||
 | 
						||
With 2.0:
 | 
						||
 | 
						||
```python
 | 
						||
class UserInput(InputObjectType):
 | 
						||
    id = ID(required=True)
 | 
						||
 | 
						||
    @property
 | 
						||
    def is_valid(root):
 | 
						||
        return root.id.startswith('userid_')
 | 
						||
 | 
						||
class Query(ObjectType):
 | 
						||
    user = graphene.Field(User, input=UserInput())
 | 
						||
 | 
						||
    def resolve_user(root, info, input):
 | 
						||
        if input.is_valid:
 | 
						||
            return get_user(input.id)
 | 
						||
```
 | 
						||
 | 
						||
### Meta as Class arguments
 | 
						||
 | 
						||
Now you can use the meta options as class arguments (**ONLY PYTHON 3**).
 | 
						||
 | 
						||
Before:
 | 
						||
 | 
						||
```python
 | 
						||
class Dog(ObjectType):
 | 
						||
    class Meta:
 | 
						||
        interfaces = [Pet]
 | 
						||
    name = String()
 | 
						||
```
 | 
						||
 | 
						||
With 2.0:
 | 
						||
 | 
						||
```python
 | 
						||
class Dog(ObjectType, interfaces=[Pet]):
 | 
						||
    name = String()
 | 
						||
```
 | 
						||
 | 
						||
### 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
 | 
						||
 | 
						||
    id = ID()
 | 
						||
 | 
						||
    def resolve_id(root, info):
 | 
						||
        return f"{root.__class__.__name__}_{root.id}"
 | 
						||
```
 | 
						||
 | 
						||
### UUID Scalar
 | 
						||
 | 
						||
In Graphene 2.0 there is a new dedicated scalar for UUIDs, `UUID`.
 |