Skip to content

Commit 2648a23

Browse files
committed
First working version of mutations
1 parent 9e10189 commit 2648a23

File tree

8 files changed

+87
-9
lines changed

8 files changed

+87
-9
lines changed

graphene/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414

1515
from graphene.core.types import (
1616
ObjectType,
17-
Interface
17+
Interface,
18+
Mutation,
1819
)
1920

2021
from graphene.core.fields import (
@@ -31,3 +32,8 @@
3132
from graphene.decorators import (
3233
resolve_only_args
3334
)
35+
36+
__all__ = ['Enum', 'Argument', 'String', 'Int', 'ID', 'signals', 'Schema',
37+
'ObjectType', 'Interface', 'Mutation', 'Field', 'StringField',
38+
'IntField', 'BooleanField', 'IDField', 'ListField', 'NonNullField',
39+
'FloatField', 'resolve_only_args']

graphene/core/fields.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,18 @@ def contribute_to_class(self, cls, name, add=True):
5151
cls._meta.add_field(self)
5252

5353
def resolve(self, instance, args, info):
54-
resolve_fn = self.get_resolve_fn()
54+
schema = info and getattr(info.schema, 'graphene_schema', None)
55+
resolve_fn = self.get_resolve_fn(schema)
5556
if resolve_fn:
5657
return resolve_fn(instance, args, info)
5758
else:
5859
return getattr(instance, self.field_name, None)
5960

60-
def get_resolve_fn(self):
61-
if self.resolve_fn:
61+
def get_resolve_fn(self, schema):
62+
object_type = self.get_object_type(schema)
63+
if object_type and object_type._meta.mutation:
64+
return object_type.mutate
65+
elif self.resolve_fn:
6266
return self.resolve_fn
6367
else:
6468
custom_resolve_fn_name = 'resolve_%s' % self.field_name
@@ -125,7 +129,7 @@ def internal_field(self, schema):
125129
raise Exception("Internal type for field %s is None" % self)
126130

127131
description = self.description
128-
resolve_fn = self.get_resolve_fn()
132+
resolve_fn = self.get_resolve_fn(schema)
129133
if resolve_fn:
130134
description = resolve_fn.__doc__ or description
131135

graphene/core/options.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from graphene.utils import cached_property
22
from collections import OrderedDict, namedtuple
33

4-
DEFAULT_NAMES = ('description', 'name', 'interface',
4+
DEFAULT_NAMES = ('description', 'name', 'interface', 'mutation',
55
'type_name', 'interfaces', 'proxy')
66

77

@@ -11,6 +11,7 @@ def __init__(self, meta=None):
1111
self.meta = meta
1212
self.local_fields = []
1313
self.interface = False
14+
self.mutation = False
1415
self.proxy = False
1516
self.interfaces = []
1617
self.parents = []

graphene/core/schema.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ def query(self, query):
4343
self._query = query
4444
self._query_type = query and query.internal_type(self)
4545

46+
@property
47+
def mutation(self):
48+
return self._mutation
49+
50+
@mutation.setter
51+
def mutation(self, mutation):
52+
self._mutation = mutation
53+
self._mutation_type = mutation and mutation.internal_type(self)
54+
4655
@property
4756
def executor(self):
4857
if not self._executor:
@@ -57,7 +66,7 @@ def executor(self, value):
5766
def schema(self):
5867
if not self._query_type:
5968
raise Exception('You have to define a base query type')
60-
return GraphQLSchema(self, query=self._query_type, mutation=self.mutation)
69+
return GraphQLSchema(self, query=self._query_type, mutation=self._mutation_type)
6170

6271
def associate_internal_type(self, internal_type, object_type):
6372
self._internal_types[internal_type.name] = object_type

graphene/core/types.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ def __new__(cls, name, bases, attrs):
5555
# Add all attributes to the class.
5656
for obj_name, obj in attrs.items():
5757
new_class.add_to_class(obj_name, obj)
58+
59+
if new_class._meta.mutation:
60+
assert hasattr(new_class, 'mutate'), "All mutations must implement mutate method"
61+
5862
new_class.add_extra_fields()
5963

6064
new_fields = new_class._meta.local_fields

tests/core/test_fields.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def test_field_resolve():
8787
f = StringField(required=True, resolve=lambda *args: 'RESOLVED')
8888
f.contribute_to_class(ot, 'field_name')
8989
field_type = f.internal_field(schema)
90-
assert 'RESOLVED' == field_type.resolver(ot, 2, 3)
90+
assert 'RESOLVED' == field_type.resolver(ot, None, None)
9191

9292

9393
def test_field_resolve_type_custom():

tests/core/test_mutations.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import graphene
2+
from py.test import raises
3+
from graphene.core.schema import Schema
4+
5+
my_id = 0
6+
7+
8+
class Query(graphene.ObjectType):
9+
base = graphene.StringField()
10+
11+
12+
class ChangeNumber(graphene.Mutation):
13+
'''Result mutation'''
14+
class Input:
15+
id = graphene.IntField(required=True)
16+
17+
result = graphene.StringField()
18+
19+
@classmethod
20+
def mutate(cls, instance, args, info):
21+
global my_id
22+
my_id = my_id + 1
23+
return ChangeNumber(result=my_id)
24+
25+
26+
class MyResultMutation(graphene.ObjectType):
27+
change_number = graphene.Field(ChangeNumber)
28+
29+
30+
schema = Schema(query=Query, mutation=MyResultMutation)
31+
32+
33+
def test_mutate():
34+
query = '''
35+
mutation M{
36+
first: changeNumber {
37+
result
38+
},
39+
second: changeNumber {
40+
result
41+
}
42+
}
43+
'''
44+
expected = {
45+
'first': {
46+
'result': '1',
47+
},
48+
'second': {
49+
'result': '2',
50+
}
51+
}
52+
result = schema.execute(query, root=object())
53+
assert not result.errors
54+
assert result.data == expected

tests/core/test_query.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def resolve_pet(self, *args):
5050

5151

5252
def test_type():
53-
assert Human._meta.fields_map['name'].resolve(Human(object()), 1, 2) == 'Peter'
53+
assert Human._meta.fields_map['name'].resolve(Human(object()), None, None) == 'Peter'
5454

5555

5656
def test_query():

0 commit comments

Comments
 (0)