Skip to content

Commit f22504c

Browse files
committed
Improved Mutation with custom Field and output
1 parent 078230a commit f22504c

File tree

3 files changed

+49
-20
lines changed

3 files changed

+49
-20
lines changed

graphene/relay/mutation.py

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55

66
from promise import Promise
77

8-
from ..types import AbstractType, Argument, Field, InputObjectType, String
9-
from ..types.objecttype import ObjectType, ObjectTypeMeta
8+
from ..types import Field, AbstractType, Argument, InputObjectType, String
9+
from ..types.mutation import Mutation, MutationMeta
10+
from ..types.objecttype import ObjectTypeMeta
1011
from ..utils.is_base_type import is_base_type
1112
from ..utils.props import props
1213

1314

14-
class ClientIDMutationMeta(ObjectTypeMeta):
15-
15+
class ClientIDMutationMeta(MutationMeta):
1616
def __new__(cls, name, bases, attrs):
1717
# Also ensure initialization is only performed for subclasses of
1818
# Mutation
@@ -23,13 +23,13 @@ def __new__(cls, name, bases, attrs):
2323
base_name = re.sub('Payload$', '', name)
2424
if 'client_mutation_id' not in attrs:
2525
attrs['client_mutation_id'] = String(name='clientMutationId')
26-
cls = ObjectTypeMeta.__new__(cls, '{}Payload'.format(base_name), bases, attrs)
26+
cls = ObjectTypeMeta.__new__(cls, '{}Payload'.format(base_name), bases,
27+
attrs)
2728
mutate_and_get_payload = getattr(cls, 'mutate_and_get_payload', None)
2829
if cls.mutate and cls.mutate.__func__ == ClientIDMutation.mutate.__func__:
2930
assert mutate_and_get_payload, (
3031
"{}.mutate_and_get_payload method is required"
31-
" in a ClientIDMutation."
32-
).format(name)
32+
" in a ClientIDMutation.").format(name)
3333
input_attrs = {}
3434
bases = ()
3535
if not input_class:
@@ -39,13 +39,18 @@ def __new__(cls, name, bases, attrs):
3939
else:
4040
bases += (input_class, )
4141
input_attrs['client_mutation_id'] = String(name='clientMutationId')
42-
cls.Input = type('{}Input'.format(base_name), bases + (InputObjectType,), input_attrs)
43-
cls.Field = partial(Field, cls, resolver=cls.mutate, input=Argument(cls.Input, required=True))
42+
cls.Input = type('{}Input'.format(base_name),
43+
bases + (InputObjectType, ), input_attrs)
44+
output_class = getattr(cls, 'Output', cls)
45+
cls.Field = partial(
46+
Field,
47+
output_class,
48+
resolver=cls.mutate,
49+
input=Argument(cls.Input, required=True))
4450
return cls
4551

4652

47-
class ClientIDMutation(six.with_metaclass(ClientIDMutationMeta, ObjectType)):
48-
53+
class ClientIDMutation(six.with_metaclass(ClientIDMutationMeta, Mutation)):
4954
@classmethod
5055
def mutate(cls, root, args, context, info):
5156
input = args.get('input')
@@ -54,11 +59,10 @@ def on_resolve(payload):
5459
try:
5560
payload.client_mutation_id = input.get('clientMutationId')
5661
except:
57-
raise Exception((
58-
'Cannot set client_mutation_id in the payload object {}'
59-
).format(repr(payload)))
62+
raise Exception(
63+
('Cannot set client_mutation_id in the payload object {}'
64+
).format(repr(payload)))
6065
return payload
6166

6267
return Promise.resolve(
63-
cls.mutate_and_get_payload(input, context, info)
64-
).then(on_resolve)
68+
cls.mutate_and_get_payload(input, context, info)).then(on_resolve)

graphene/types/mutation.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111

1212
class MutationMeta(ObjectTypeMeta):
13-
1413
def __new__(cls, name, bases, attrs):
1514
# Also ensure initialization is only performed for subclasses of
1615
# Mutation
@@ -21,10 +20,12 @@ def __new__(cls, name, bases, attrs):
2120

2221
cls = ObjectTypeMeta.__new__(cls, name, bases, attrs)
2322
field_args = props(input_class) if input_class else {}
23+
output_class = getattr(cls, 'Output', cls)
2424
resolver = getattr(cls, 'mutate', None)
2525
assert resolver, 'All mutations must define a mutate method in it'
2626
resolver = get_unbound_function(resolver)
27-
cls.Field = partial(Field, cls, args=field_args, resolver=resolver)
27+
cls.Field = partial(
28+
Field, output_class, args=field_args, resolver=resolver)
2829
return cls
2930

3031

graphene/types/tests/test_mutation.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
from ..mutation import Mutation
44
from ..objecttype import ObjectType
55
from ..schema import Schema
6+
from ..argument import Argument
67
from ..scalars import String
78
from ..dynamic import Dynamic
89

910

1011
def test_generate_mutation_no_args():
1112
class MyMutation(Mutation):
1213
'''Documentation'''
14+
1315
@classmethod
1416
def mutate(cls, *args, **kwargs):
1517
pass
@@ -22,7 +24,6 @@ def mutate(cls, *args, **kwargs):
2224

2325
def test_generate_mutation_with_meta():
2426
class MyMutation(Mutation):
25-
2627
class Meta:
2728
name = 'MyOtherMutation'
2829
description = 'Documentation'
@@ -38,10 +39,33 @@ def mutate(cls, *args, **kwargs):
3839

3940
def test_mutation_raises_exception_if_no_mutate():
4041
with pytest.raises(AssertionError) as excinfo:
42+
4143
class MyMutation(Mutation):
4244
pass
4345

44-
assert "All mutations must define a mutate method in it" == str(excinfo.value)
46+
assert "All mutations must define a mutate method in it" == str(
47+
excinfo.value)
48+
49+
50+
def test_mutation_custom_output_type():
51+
class User(ObjectType):
52+
name = String()
53+
54+
class CreateUser(Mutation):
55+
class Input:
56+
name = String()
57+
58+
Output = User
59+
60+
@classmethod
61+
def mutate(cls, args, context, info):
62+
name = args.get('name')
63+
return User(name=name)
64+
65+
field = CreateUser.Field()
66+
assert field.type == User
67+
assert field.args == {'name': Argument(String)}
68+
assert field.resolver == CreateUser.mutate
4569

4670

4771
def test_mutation_execution():

0 commit comments

Comments
 (0)