Let's say I have an InputObjectType:
class MyIOT(graphene.InputObjectType):
my_field = graphene.String()
Directly instantiating that as an instance container gives me a half implementation:
my_iot = MyIOT(my_field='asdf')
Yet, when it's constructed by graphene I'm able to access a full implementation, with methods like keys(). I've been hunting through the code for a couple hours, but can't seem to figure out where it's created. Any ideas?
I'm a little bit closer. Getting the InputObjectType from the schema, and using create_container gives something a bit more useful (a fully structured InputObjectType):
MyIOT = schema.get_type('MyIOT')
MyIOT.create_container({'my_field': 'asdf'})
However, with a more complex example:
class ChildIOT(graphene.InputObjectType):
nested_field = graphene.String()
@property
def invert_nested(self):
return self.nested_field[::-1]
class ParentIOT(graphene.InputObjectType):
root_field = graphene.String()
child_field = graphene.Field(ChildIOT)
# ... create schema, etc.
FullParentIOT = schema.get_type('ParentIOT') # is this actually called "mounted"?
fp_iot = FullParentIOT.create_container({
'root_field': 'i am root',
'child_field': {'nested_field': 'i am nested'},
})
assert type(fp_iot.child_field) == dict # unfortunately, this is not a hydrated / full ChildIOT instance!
# fp_iot.child_field.invert_nested # of course, we can't access ChildIOT methods on a dict instance.
So, how do I build a complex InputObjectType? Do I need to manually build the subcomponents and then inject them?
Here's the answer (look at synthesize_input_container):
import graphene
def resolve_type_heirarchy(obj):
if isinstance(obj, graphene.Field):
nested_type = obj.type
type_ = graphene.Field
elif isinstance(obj, graphene.InputField):
nested_type = obj.type
type_ = graphene.InputField
elif isinstance(obj, graphene.List):
nested_type = obj.of_type
type_ = graphene.List
elif isinstance(obj, graphene.NonNull):
nested_type = obj.of_type
type_ = graphene.NonNull
else:
return (obj,)
return (type_, *resolve_type_heirarchy(nested_type))
def resolve_leaf_type(obj):
return resolve_type_heirarchy(obj)[-1]
def is_collection(obj):
return graphene.List in resolve_type_heirarchy(obj)
def synthesize_input_container(input_object_type_cls, data):
# pylint: disable=protected-access
for field_name, value in data.items():
field = input_object_type_cls._meta.fields[field_name]
if is_collection(field):
items = []
for item in value:
leaf_type = resolve_leaf_type(field)
if issubclass(leaf_type, graphene.InputObjectType):
item = synthesize_input_container(leaf_type, item)
items.append(item)
data[field_name] = items
else:
if issubclass(field.type, graphene.InputObjectType):
data[field_name] = synthesize_input_container(field.type, value)
return input_object_type_cls._meta.container(data)
for use like:
def test_synthesize_input_container():
class RelatedInput(graphene.InputObjectType):
id = graphene.ID()
class Input(graphene.InputObjectType):
id = graphene.ID()
ids = graphene.List(graphene.NonNull(graphene.ID))
related = RelatedInput()
relateds = graphene.List(graphene.NonNull(RelatedInput))
input_raw = {
'id': 'input_id_1',
'ids': ['input_id_2',],
'related': {'id': 'related_input_id_1'},
'relateds': [{'id': 'related_input_id_2'}],
}
input_container = synthesize_input_container(Input, input_raw)
assert isinstance(input_container, Input)
assert input_container.id == input_raw['id']
assert isinstance(input_container.ids, list)
assert len(input_container.ids) == 1
assert input_container.ids[0] == input_raw['ids'][0]
assert isinstance(input_container.related, RelatedInput)
assert input_container.related.id == input_raw['related']['id']
assert isinstance(input_container.relateds, list)
assert len(input_container.relateds) == 1
assert isinstance(input_container.relateds[0], RelatedInput)
assert input_container.relateds[0].id == input_raw['relateds'][0]['id']
Great implementation, thank you it really helped me!
@dfee, you managed to make this work when using a graphene DateTime scalar? I could not make it with your solution.
Most helpful comment
Here's the answer (look at
synthesize_input_container):for use like: