Repository: graphql-python/graphene
Pull request #596 (2017-11-03 23:38:48)

Set all fields on input object types
Previously, this only set fields that were explicitly provided in the query.  Optional input fields that were _not_ provided in the query were left as Graphene field definition types (such as `List` or `ID`).


Patch:
diff --git a/graphene/types/inputobjecttype.py b/graphene/types/inputobjecttype.py
index 38173c7..cf76442 100644
--- a/graphene/types/inputobjecttype.py
+++ b/graphene/types/inputobjecttype.py
@@ -2,10 +2,10 @@ from collections import OrderedDict
 
 from .base import BaseOptions, BaseType
 from .inputfield import InputField
+from .structures import List, NonNull
 from .unmountedtype import UnmountedType
 from .utils import yank_fields_from_attrs
 
-
 # For static type checking with Mypy
 MYPY = False
 if MYPY:
@@ -23,12 +23,31 @@ class InputObjectTypeContainer(dict, BaseType):
 
     def __init__(self, *args, **kwargs):
         dict.__init__(self, *args, **kwargs)
-        for key, value in self.items():
+        for key in self._meta.fields.keys():
+            field = getattr(self, key, None)
+            if field is None or self.get(key, None) is None:
+                value = None
+            else:
+                value = InputObjectTypeContainer._get_typed_field_value(field, self[key])
             setattr(self, key, value)
 
     def __init_subclass__(cls, *args, **kwargs):
         pass
 
+    @staticmethod
+    def _get_typed_field_value(field_or_type, value):
+        if isinstance(field_or_type, NonNull):
+            return InputObjectTypeContainer._get_typed_field_value(field_or_type.of_type, value)
+        elif isinstance(field_or_type, List):
+            return [
+                InputObjectTypeContainer._get_typed_field_value(field_or_type.of_type, v)
+                for v in value
+            ]
+        elif hasattr(field_or_type, '_meta') and hasattr(field_or_type._meta, 'container'):
+            return field_or_type._meta.container(value)
+        else:
+            return value
+
 
 class InputObjectType(UnmountedType, BaseType):
     '''


Test patch:
diff --git a/graphene/types/tests/test_typemap.py b/graphene/types/tests/test_typemap.py
index 082f25b..fe8e99b 100644
--- a/graphene/types/tests/test_typemap.py
+++ b/graphene/types/tests/test_typemap.py
@@ -4,6 +4,7 @@ from graphql.type import (GraphQLArgument, GraphQLEnumType, GraphQLEnumValue,
                           GraphQLInputObjectType, GraphQLInterfaceType,
                           GraphQLObjectType, GraphQLString)
 
+from ..structures import List, NonNull
 from ..dynamic import Dynamic
 from ..enum import Enum
 from ..field import Field
@@ -11,7 +12,7 @@ from ..inputfield import InputField
 from ..inputobjecttype import InputObjectType
 from ..interface import Interface
 from ..objecttype import ObjectType
-from ..scalars import String
+from ..scalars import String, Int
 from ..typemap import TypeMap
 
 
@@ -119,10 +120,18 @@ def test_interface():
 
 
 def test_inputobject():
+    class OtherObjectType(InputObjectType):
+        thingy = NonNull(Int)
+
+    class MyInnerObjectType(InputObjectType):
+        some_field = String()
+        some_other_field = List(OtherObjectType)
+
     class MyInputObjectType(InputObjectType):
         '''Description'''
         foo_bar = String(description='Field description')
         bar = String(name='gizmo')
+        baz = NonNull(MyInnerObjectType)
         own = InputField(lambda: MyInputObjectType)
 
         def resolve_foo_bar(self, args, info):
@@ -136,14 +145,23 @@ def test_inputobject():
     assert graphql_type.description == 'Description'
 
     # Container
-    container = graphql_type.create_container({'bar': 'oh!'})
+    container = graphql_type.create_container({
+        'bar': 'oh!',
+        'baz': {
+            'some_other_field': [{'thingy': 1}, {'thingy': 2}]
+        }
+    })
     assert isinstance(container, MyInputObjectType)
     assert 'bar' in container
     assert container.bar == 'oh!'
     assert 'foo_bar' not in container
+    assert container.foo_bar is None
+    assert container.baz.some_field is None
+    assert container.baz.some_other_field[0].thingy == 1
+    assert container.baz.some_other_field[1].thingy == 2
 
     fields = graphql_type.fields
-    assert list(fields.keys()) == ['fooBar', 'gizmo', 'own']
+    assert list(fields.keys()) == ['fooBar', 'gizmo', 'baz', 'own']
     own_field = fields['own']
     assert own_field.type == graphql_type
     foo_field = fields['fooBar']