diff --git a/graphene/__init__.py b/graphene/__init__.py index 3178c08c..01e9a28d 100644 --- a/graphene/__init__.py +++ b/graphene/__init__.py @@ -9,7 +9,8 @@ from .types import ( String, ID, Int, Float, Boolean, List, NonNull, Enum, - Argument + Argument, + Dynamic ) from .utils.resolve_only_args import resolve_only_args @@ -30,4 +31,5 @@ __all__ = [ 'List', 'NonNull', 'Argument', + 'Dynamic', 'resolve_only_args'] diff --git a/graphene/types/__init__.py b/graphene/types/__init__.py index 9523d7fd..f81a6db2 100644 --- a/graphene/types/__init__.py +++ b/graphene/types/__init__.py @@ -8,6 +8,7 @@ from .enum import Enum from .field import Field from .inputfield import InputField from .argument import Argument +from .dynamic import Dynamic from .inputobjecttype import InputObjectType __all__ = [ diff --git a/graphene/types/dynamic.py b/graphene/types/dynamic.py new file mode 100644 index 00000000..2708367b --- /dev/null +++ b/graphene/types/dynamic.py @@ -0,0 +1,13 @@ +import inspect +from ..utils.orderedtype import OrderedType + + +class Dynamic(OrderedType): + + def __init__(self, type, _creation_counter=None): + super(Dynamic, self).__init__(_creation_counter=_creation_counter) + assert inspect.isfunction(type) + self.type = type + + def get_type(self): + return self.type() diff --git a/graphene/types/tests/test_typemap.py b/graphene/types/tests/test_typemap.py index f2eed933..61ff7d12 100644 --- a/graphene/types/tests/test_typemap.py +++ b/graphene/types/tests/test_typemap.py @@ -2,6 +2,8 @@ import pytest from ..objecttype import ObjectType from ..union import Union +from ..field import Field +from ..dynamic import Dynamic from ..enum import Enum from ..typemap import TypeMap from ..scalars import String @@ -63,3 +65,18 @@ def test_objecttype(): assert foo_field.args == { 'bar': GraphQLArgument(GraphQLString, description='Argument description', default_value='x') } + + +def test_dynamic_objecttype(): + class MyObjectType(ObjectType): + '''Description''' + bar = Dynamic(lambda: Field(String)) + + typemap = TypeMap([MyObjectType]) + assert 'MyObjectType' in typemap + assert MyObjectType._meta.fields.keys() == ['bar'] + graphql_type = typemap['MyObjectType'] + + fields = graphql_type.get_fields() + assert fields.keys() == ['bar'] + assert fields['bar'].type == GraphQLString diff --git a/graphene/types/typemap.py b/graphene/types/typemap.py index de3c02d6..42b1430d 100644 --- a/graphene/types/typemap.py +++ b/graphene/types/typemap.py @@ -11,6 +11,7 @@ from .inputobjecttype import InputObjectType from .structures import List, NonNull from .enum import Enum from .scalars import Scalar, String, Boolean, Int, Float, ID +from .dynamic import Dynamic from graphql import GraphQLString, GraphQLField, GraphQLList, GraphQLBoolean, GraphQLInt, GraphQLFloat, GraphQLID, GraphQLNonNull, GraphQLInputObjectField, GraphQLArgument from graphql.type import GraphQLEnumValue @@ -189,6 +190,10 @@ class TypeMap(GraphQLTypeMap): def construct_fields_for_type(cls, map, type, is_input_type=False): fields = OrderedDict() for name, field in type._meta.fields.items(): + if isinstance(field, Dynamic): + field = field.get_type() + if not field: + continue map = cls.reducer(map, field.type) field_type = cls.get_field_type(map, field.type) if is_input_type: diff --git a/graphene/types/utils.py b/graphene/types/utils.py index 1d9069bb..5217bdfa 100644 --- a/graphene/types/utils.py +++ b/graphene/types/utils.py @@ -2,6 +2,7 @@ from collections import OrderedDict from .unmountedtype import UnmountedType from .field import Field +from .dynamic import Dynamic from .inputfield import InputField @@ -50,7 +51,7 @@ def unmounted_field_in_type(attname, unmounted_field, type): def get_fields_in_type(in_type, attrs): fields_with_names = [] for attname, value in list(attrs.items()): - if isinstance(value, (Field, InputField)): + if isinstance(value, (Field, InputField, Dynamic)): fields_with_names.append( (attname, value) )