Improved ObjectType implementation

This commit is contained in:
Syrus Akbary 2016-06-13 19:58:11 -07:00
parent d8201c44fa
commit cf80f87dca
4 changed files with 53 additions and 42 deletions

View File

@ -2,7 +2,7 @@ import six
from graphql import GraphQLInputObjectType from graphql import GraphQLInputObjectType
from .definitions import FieldsMeta, ClassTypeMeta, GrapheneGraphQLType from .definitions import GrapheneGraphQLType
from .interface import attrs_without_fields from .interface import attrs_without_fields
from .unmountedtype import UnmountedType from .unmountedtype import UnmountedType
from .options import Options from .options import Options
@ -48,7 +48,7 @@ class InputObjectTypeMeta(type):
) )
else: else:
assert not fields, "Can't mount InputFields in an InputObjectType with a defined graphql_type" assert not fields, "Can't mount InputFields in an InputObjectType with a defined graphql_type"
fields = copy_fields(options.graphql_type.get_fields(), parent=cls) fields = copy_fields(InputField, options.graphql_type.get_fields(), parent=cls)
for name, field in fields.items(): for name, field in fields.items():
setattr(cls, field.attname or name, field) setattr(cls, field.attname or name, field)

View File

@ -1,11 +1,15 @@
from itertools import chain
import copy import copy
import six import six
from graphql import GraphQLObjectType from graphql import GraphQLObjectType
from .definitions import FieldsMeta, ClassTypeMeta, GrapheneGraphQLType from .definitions import GrapheneGraphQLType
from .interface import GrapheneInterfaceType, InterfaceTypeMeta, Interface from .interface import GrapheneInterfaceType, InterfaceTypeMeta, Interface, attrs_without_fields
from .options import Options
from ..utils.is_base_type import is_base_type
from ..utils.get_fields import get_fields
from ..utils.copy_fields import copy_fields
from .field import Field
class GrapheneObjectType(GrapheneGraphQLType, GraphQLObjectType): class GrapheneObjectType(GrapheneGraphQLType, GraphQLObjectType):
@ -38,7 +42,7 @@ class GrapheneObjectType(GrapheneGraphQLType, GraphQLObjectType):
return False return False
def get_interfaces(cls, interfaces): def get_interfaces(interfaces):
from ..utils.get_graphql_type import get_graphql_type from ..utils.get_graphql_type import get_graphql_type
for interface in interfaces: for interface in interfaces:
@ -46,48 +50,58 @@ def get_interfaces(cls, interfaces):
yield graphql_type yield graphql_type
class ObjectTypeMeta(FieldsMeta, ClassTypeMeta, InterfaceTypeMeta): # We inherit from InterfaceTypeMeta instead of type for being able
# to have ObjectTypes extending Interfaces using Python syntax, like:
# class MyObjectType(ObjectType, MyInterface)
class ObjectTypeMeta(InterfaceTypeMeta):
def get_options(cls, meta): def __new__(cls, name, bases, attrs):
return cls.options_class( super_new = type.__new__
meta,
# Also ensure initialization is only performed for subclasses of Model
# (excluding Model class itself).
if not is_base_type(bases, ObjectTypeMeta):
return super_new(cls, name, bases, attrs)
options = Options(
attrs.pop('Meta', None),
name=None, name=None,
description=None, description=None,
graphql_type=None, graphql_type=None,
interfaces=[], interfaces=(),
abstract=False abstract=False
) )
interfaces = tuple(options.interfaces)
fields = get_fields(ObjectType, attrs, bases, interfaces)
attrs = attrs_without_fields(attrs, fields)
cls = super_new(cls, name, bases, dict(attrs, _meta=options))
if not options.graphql_type:
fields = copy_fields(Field, fields, parent=cls)
base_interfaces = tuple(b for b in bases if issubclass(b, Interface))
options.graphql_type = GrapheneObjectType(
graphene_type=cls,
name=options.name or cls.__name__,
description=options.description or cls.__doc__,
fields=fields,
interfaces=tuple(get_interfaces(interfaces + base_interfaces))
)
else:
assert not fields, "Can't mount Fields in an ObjectType with a defined graphql_type"
fields = copy_fields(Field, options.graphql_type.get_fields(), parent=cls)
for name, field in fields.items():
setattr(cls, field.attname or name, field)
return cls
def get_interfaces(cls, bases): def get_interfaces(cls, bases):
return (b for b in bases if issubclass(b, Interface)) return (b for b in bases if issubclass(b, Interface))
def is_object_type(cls): def is_object_type(cls):
return issubclass(cls, ObjectType) return issubclass(cls, ObjectType)
def construct(cls, bases, attrs):
if not cls._meta.abstract and cls.is_object_type():
cls.get_interfaces(bases)
interfaces = tuple(get_interfaces(cls, chain(
cls._meta.interfaces,
cls.get_interfaces(bases),
)))
local_fields = cls._extract_local_fields(attrs)
if not cls._meta.graphql_type:
cls = super(ObjectTypeMeta, cls).construct(bases, attrs)
cls._meta.graphql_type = GrapheneObjectType(
graphene_type=cls,
name=cls._meta.name or cls.__name__,
description=cls._meta.description or cls.__doc__,
fields=cls._fields(bases, attrs, local_fields, interfaces),
interfaces=interfaces,
)
return cls
else:
assert not local_fields, "Can't mount Fields in an ObjectType with a defined graphql_type"
return super(ObjectTypeMeta, cls).construct(bases, attrs)
def implements(*interfaces): def implements(*interfaces):
# This function let us decorate a ObjectType # This function let us decorate a ObjectType
@ -106,9 +120,6 @@ def implements(*interfaces):
class ObjectType(six.with_metaclass(ObjectTypeMeta)): class ObjectType(six.with_metaclass(ObjectTypeMeta)):
class Meta:
abstract = True
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
# GraphQL ObjectType acting as container # GraphQL ObjectType acting as container
args_len = len(args) args_len = len(args)

View File

@ -26,12 +26,12 @@ def get_fields_from_types(bases):
yield attname, field yield attname, field
def get_fields(in_type, attrs, bases): def get_fields(in_type, attrs, bases, graphql_types=()):
fields = [] fields = []
graphene_bases = tuple( graphene_bases = tuple(
base._meta.graphql_type for base in bases if is_graphene_type(base) and not base._meta.abstract base._meta.graphql_type for base in bases if is_graphene_type(base)
) ) + graphql_types
extended_fields = list(get_fields_from_types(graphene_bases)) extended_fields = list(get_fields_from_types(graphene_bases))
local_fields = list(get_fields_from_attrs(in_type, attrs)) local_fields = list(get_fields_from_attrs(in_type, attrs))

View File

@ -8,7 +8,7 @@ def is_graphene_type(_type):
from ..types.scalars import Scalar from ..types.scalars import Scalar
from ..types.enum import Enum from ..types.enum import Enum
if _type in [Interface, InputObjectType]: if _type in [Interface, InputObjectType, ObjectType]:
return False return False
return inspect.isclass(_type) and issubclass(_type, ( return inspect.isclass(_type) and issubclass(_type, (
Interface, Interface,