Skip to content

Commit d9b8f59

Browse files
committed
Added default value for default resolver.
1 parent 316569b commit d9b8f59

File tree

5 files changed

+73
-11
lines changed

5 files changed

+73
-11
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ target/
7575

7676
# PyCharm
7777
.idea
78+
*.iml
7879

7980
# Databases
8081
*.sqlite3

graphene/types/field.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,23 @@ def source_resolver(source, root, args, context, info):
1717

1818
class Field(OrderedType):
1919

20-
def __init__(self, type, args=None, resolver=None, source=None,
20+
def __init__(self, gql_type, args=None, resolver=None, source=None,
2121
deprecation_reason=None, name=None, description=None,
22-
required=False, _creation_counter=None, **extra_args):
22+
required=False, _creation_counter=None, default_value=None,
23+
**extra_args):
2324
super(Field, self).__init__(_creation_counter=_creation_counter)
2425
assert not args or isinstance(args, Mapping), (
2526
'Arguments in a field have to be a mapping, received "{}".'
2627
).format(args)
2728
assert not (source and resolver), (
2829
'A Field cannot have a source and a resolver in at the same time.'
2930
)
31+
assert not callable(default_value), (
32+
'The default value can not be a function but received "{}".'
33+
).format(type(default_value))
3034

3135
if required:
32-
type = NonNull(type)
36+
gql_type = NonNull(gql_type)
3337

3438
# Check if name is actually an argument of the field
3539
if isinstance(name, (Argument, UnmountedType)):
@@ -42,13 +46,14 @@ def __init__(self, type, args=None, resolver=None, source=None,
4246
source = None
4347

4448
self.name = name
45-
self._type = type
49+
self._type = gql_type
4650
self.args = to_arguments(args or OrderedDict(), extra_args)
4751
if source:
4852
resolver = partial(source_resolver, source)
4953
self.resolver = resolver
5054
self.deprecation_reason = deprecation_reason
5155
self.description = description
56+
self.default_value = default_value
5257

5358
@property
5459
def type(self):

graphene/types/tests/test_field.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,22 @@ def test_field_basic():
1616
resolver = lambda: None
1717
deprecation_reason = 'Deprecated now'
1818
description = 'My Field'
19+
my_default='something'
1920
field = Field(
2021
MyType,
2122
name='name',
2223
args=args,
2324
resolver=resolver,
2425
description=description,
25-
deprecation_reason=deprecation_reason
26+
deprecation_reason=deprecation_reason,
27+
default_value=my_default,
2628
)
2729
assert field.name == 'name'
2830
assert field.args == args
2931
assert field.resolver == resolver
3032
assert field.deprecation_reason == deprecation_reason
3133
assert field.description == description
34+
assert field.default_value == my_default
3235

3336

3437
def test_field_required():
@@ -38,6 +41,15 @@ def test_field_required():
3841
assert field.type.of_type == MyType
3942

4043

44+
def test_field_default_value_not_callable():
45+
MyType = object()
46+
try:
47+
Field(MyType, default_value=lambda: True)
48+
except AssertionError as e:
49+
# substring comparison for py 2/3 compatibility
50+
assert 'The default value can not be a function but received' in str(e)
51+
52+
4153
def test_field_source():
4254
MyType = object()
4355
field = Field(MyType, source='value')

graphene/types/tests/test_query.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import json
22
from functools import partial
33

4-
from graphql import Source, execute, parse
4+
from graphql import Source, execute, parse, GraphQLError
55

6+
from ..field import Field
67
from ..inputfield import InputField
78
from ..inputobjecttype import InputObjectType
89
from ..objecttype import ObjectType
@@ -22,6 +23,49 @@ class Query(ObjectType):
2223
assert executed.data == {'hello': 'World'}
2324

2425

26+
def test_query_default_value():
27+
class MyType(ObjectType):
28+
field = String()
29+
30+
class Query(ObjectType):
31+
hello = Field(MyType, default_value=MyType(field='something else!'))
32+
33+
hello_schema = Schema(Query)
34+
35+
executed = hello_schema.execute('{ hello { field } }')
36+
assert not executed.errors
37+
assert executed.data == {'hello': {'field': 'something else!'}}
38+
39+
40+
def test_query_wrong_default_value():
41+
class MyType(ObjectType):
42+
field = String()
43+
44+
class Query(ObjectType):
45+
hello = Field(MyType, default_value='hello')
46+
47+
hello_schema = Schema(Query)
48+
49+
executed = hello_schema.execute('{ hello { field } }')
50+
assert len(executed.errors) == 1
51+
assert executed.errors[0].message == GraphQLError('Expected value of type "MyType" but got: str.').message
52+
assert executed.data == {'hello': None}
53+
54+
55+
def test_query_default_value_ignored_by_resolver():
56+
class MyType(ObjectType):
57+
field = String()
58+
59+
class Query(ObjectType):
60+
hello = Field(MyType, default_value='hello', resolver=lambda *_: MyType(field='no default.'))
61+
62+
hello_schema = Schema(Query)
63+
64+
executed = hello_schema.execute('{ hello { field } }')
65+
assert not executed.errors
66+
assert executed.data == {'hello': {'field': 'no default.'}}
67+
68+
2569
def test_query_resolve_function():
2670
class Query(ObjectType):
2771
hello = String()

graphene/types/typemap.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ def get_name(self, name):
190190
return to_camel_case(name)
191191
return name
192192

193-
def default_resolver(self, attname, root, *_):
194-
return getattr(root, attname, None)
193+
def default_resolver(self, attname, default_value, root, *_):
194+
return getattr(root, attname, default_value)
195195

196196
def construct_fields_for_type(self, map, type, is_input_type=False):
197197
fields = OrderedDict()
@@ -224,15 +224,15 @@ def construct_fields_for_type(self, map, type, is_input_type=False):
224224
_field = GraphQLField(
225225
field_type,
226226
args=args,
227-
resolver=field.get_resolver(self.get_resolver_for_type(type, name)),
227+
resolver=field.get_resolver(self.get_resolver_for_type(type, name, field.default_value)),
228228
deprecation_reason=field.deprecation_reason,
229229
description=field.description
230230
)
231231
field_name = field.name or self.get_name(name)
232232
fields[field_name] = _field
233233
return fields
234234

235-
def get_resolver_for_type(self, type, name):
235+
def get_resolver_for_type(self, type, name, default_value):
236236
if not issubclass(type, ObjectType):
237237
return
238238
resolver = getattr(type, 'resolve_{}'.format(name), None)
@@ -253,7 +253,7 @@ def get_resolver_for_type(self, type, name):
253253
return resolver.__func__
254254
return resolver
255255

256-
return partial(self.default_resolver, name)
256+
return partial(self.default_resolver, name, default_value)
257257

258258
def get_field_type(self, map, type):
259259
if isinstance(type, List):

0 commit comments

Comments
 (0)