mirror of
https://github.com/graphql-python/graphene.git
synced 2025-02-18 04:20:36 +03:00
Completed Django support. Improved tests. Changed schema behavior
This commit is contained in:
parent
72c88a19e5
commit
c945df6064
|
@ -4,7 +4,8 @@ python:
|
||||||
- 2.7
|
- 2.7
|
||||||
install:
|
install:
|
||||||
- pip install pytest pytest-cov coveralls flake8 six blinker
|
- pip install pytest pytest-cov coveralls flake8 six blinker
|
||||||
- pip install -e .[django]
|
# - pip install -e .[django] # TODO: Commented until graphqllib is in pypi
|
||||||
|
- pip install Django>=1.8.0 pytest-django singledispatch>=3.4.0.3
|
||||||
- pip install git+https://github.com/dittos/graphqllib.git # Last version of graphqllib
|
- pip install git+https://github.com/dittos/graphqllib.git # Last version of graphqllib
|
||||||
- pip install graphql-relay
|
- pip install graphql-relay
|
||||||
- python setup.py develop
|
- python setup.py develop
|
||||||
|
|
|
@ -35,4 +35,4 @@ from graphene.decorators import (
|
||||||
resolve_only_args
|
resolve_only_args
|
||||||
)
|
)
|
||||||
|
|
||||||
import graphene.relay
|
# import graphene.relay
|
||||||
|
|
|
@ -4,9 +4,11 @@ from graphene.core.fields import (
|
||||||
from graphene import relay
|
from graphene import relay
|
||||||
|
|
||||||
from graphene.core.fields import Field, LazyField
|
from graphene.core.fields import Field, LazyField
|
||||||
from graphene.utils import cached_property
|
from graphene.utils import cached_property, memoize
|
||||||
from graphene.env import get_global_schema
|
from graphene.env import get_global_schema
|
||||||
|
|
||||||
|
from graphene.relay.types import BaseNode
|
||||||
|
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
from django.db.models.manager import Manager
|
from django.db.models.manager import Manager
|
||||||
|
|
||||||
|
@ -29,11 +31,11 @@ class DjangoConnectionField(relay.ConnectionField):
|
||||||
|
|
||||||
|
|
||||||
class ConnectionOrListField(LazyField):
|
class ConnectionOrListField(LazyField):
|
||||||
def get_field(self):
|
@memoize
|
||||||
schema = self.schema
|
def get_field(self, schema):
|
||||||
model_field = self.field_type
|
model_field = self.field_type
|
||||||
field_object_type = model_field.get_object_type()
|
field_object_type = model_field.get_object_type(schema)
|
||||||
if field_object_type and issubclass(field_object_type, schema.Node):
|
if field_object_type and issubclass(field_object_type, BaseNode):
|
||||||
field = DjangoConnectionField(model_field)
|
field = DjangoConnectionField(model_field)
|
||||||
else:
|
else:
|
||||||
field = ListField(model_field)
|
field = ListField(model_field)
|
||||||
|
@ -46,13 +48,13 @@ class DjangoModelField(Field):
|
||||||
super(DjangoModelField, self).__init__(None, *args, **kwargs)
|
super(DjangoModelField, self).__init__(None, *args, **kwargs)
|
||||||
self.model = model
|
self.model = model
|
||||||
|
|
||||||
@cached_property
|
@memoize
|
||||||
def type(self):
|
def internal_type(self, schema):
|
||||||
_type = self.get_object_type()
|
_type = self.get_object_type(schema)
|
||||||
return _type and _type._meta.type
|
return _type and _type.internal_type(schema)
|
||||||
|
|
||||||
def get_object_type(self):
|
def get_object_type(self, schema):
|
||||||
_type = get_type_for_model(self.schema, self.model)
|
_type = get_type_for_model(schema, self.model)
|
||||||
if not _type and self.object_type._meta.only_fields:
|
if not _type and self.object_type._meta.only_fields:
|
||||||
# We will only raise the exception if the related field is specified in only_fields
|
# We will only raise the exception if the related field is specified in only_fields
|
||||||
raise Exception("Field %s (%s) model not mapped in current schema" % (self, self.model._meta.object_name))
|
raise Exception("Field %s (%s) model not mapped in current schema" % (self, self.model._meta.object_name))
|
||||||
|
|
|
@ -5,6 +5,7 @@ from graphene.core.options import Options
|
||||||
|
|
||||||
VALID_ATTRS = ('model', 'only_fields')
|
VALID_ATTRS = ('model', 'only_fields')
|
||||||
|
|
||||||
|
from graphene.relay.types import Node, BaseNode
|
||||||
|
|
||||||
class DjangoOptions(Options):
|
class DjangoOptions(Options):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -15,9 +16,9 @@ class DjangoOptions(Options):
|
||||||
|
|
||||||
def contribute_to_class(self, cls, name):
|
def contribute_to_class(self, cls, name):
|
||||||
super(DjangoOptions, self).contribute_to_class(cls, name)
|
super(DjangoOptions, self).contribute_to_class(cls, name)
|
||||||
if self.proxy:
|
if cls.__name__ == 'DjangoNode':
|
||||||
return
|
return
|
||||||
if not self.model:
|
if not self.model:
|
||||||
raise Exception('Django ObjectType %s must have a model in the Meta attr' % cls)
|
raise Exception('Django ObjectType %s must have a model in the Meta class attr' % cls)
|
||||||
elif not inspect.isclass(self.model) or not issubclass(self.model, models.Model):
|
elif not inspect.isclass(self.model) or not issubclass(self.model, models.Model):
|
||||||
raise Exception('Provided model in %s is not a Django model' % cls)
|
raise Exception('Provided model in %s is not a Django model' % cls)
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import six
|
import six
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from graphene.core.types import ObjectTypeMeta, ObjectType
|
from graphene.core.types import ObjectTypeMeta, BaseObjectType
|
||||||
from graphene.contrib.django.options import DjangoOptions
|
from graphene.contrib.django.options import DjangoOptions
|
||||||
from graphene.contrib.django.converter import convert_django_field
|
from graphene.contrib.django.converter import convert_django_field
|
||||||
|
|
||||||
from graphene.relay import Node
|
from graphene.relay.types import Node, BaseNode
|
||||||
|
|
||||||
|
|
||||||
def get_reverse_fields(model):
|
def get_reverse_fields(model):
|
||||||
|
@ -18,6 +18,9 @@ def get_reverse_fields(model):
|
||||||
class DjangoObjectTypeMeta(ObjectTypeMeta):
|
class DjangoObjectTypeMeta(ObjectTypeMeta):
|
||||||
options_cls = DjangoOptions
|
options_cls = DjangoOptions
|
||||||
|
|
||||||
|
def is_interface(cls, parents):
|
||||||
|
return DjangoInterface in parents
|
||||||
|
|
||||||
def add_extra_fields(cls):
|
def add_extra_fields(cls):
|
||||||
if not cls._meta.model:
|
if not cls._meta.model:
|
||||||
return
|
return
|
||||||
|
@ -30,11 +33,13 @@ class DjangoObjectTypeMeta(ObjectTypeMeta):
|
||||||
cls.add_to_class(field.name, converted_field)
|
cls.add_to_class(field.name, converted_field)
|
||||||
|
|
||||||
|
|
||||||
class DjangoObjectType(six.with_metaclass(DjangoObjectTypeMeta, ObjectType)):
|
class DjangoObjectType(six.with_metaclass(DjangoObjectTypeMeta, BaseObjectType)):
|
||||||
class Meta:
|
pass
|
||||||
proxy = True
|
|
||||||
|
|
||||||
|
|
||||||
class DjangoNode(six.with_metaclass(DjangoObjectTypeMeta, Node)):
|
class DjangoInterface(six.with_metaclass(DjangoObjectTypeMeta, BaseObjectType)):
|
||||||
class Meta:
|
pass
|
||||||
proxy = True
|
|
||||||
|
|
||||||
|
class DjangoNode(BaseNode, DjangoInterface):
|
||||||
|
pass
|
|
@ -10,8 +10,8 @@ from graphql.core.type import (
|
||||||
GraphQLArgument,
|
GraphQLArgument,
|
||||||
GraphQLFloat,
|
GraphQLFloat,
|
||||||
)
|
)
|
||||||
from graphene.utils import cached_property
|
from graphene.utils import cached_property, memoize
|
||||||
from graphene.core.types import ObjectType
|
from graphene.core.types import BaseObjectType
|
||||||
|
|
||||||
class Field(object):
|
class Field(object):
|
||||||
def __init__(self, field_type, resolve=None, null=True, args=None, description='', **extra_args):
|
def __init__(self, field_type, resolve=None, null=True, args=None, description='', **extra_args):
|
||||||
|
@ -27,7 +27,6 @@ class Field(object):
|
||||||
def contribute_to_class(self, cls, name):
|
def contribute_to_class(self, cls, name):
|
||||||
self.field_name = name
|
self.field_name = name
|
||||||
self.object_type = cls
|
self.object_type = cls
|
||||||
self.schema = cls._meta.schema
|
|
||||||
if isinstance(self.field_type, Field) and not self.field_type.object_type:
|
if isinstance(self.field_type, Field) and not self.field_type.object_type:
|
||||||
self.field_type.contribute_to_class(cls, name)
|
self.field_type.contribute_to_class(cls, name)
|
||||||
cls._meta.add_field(self)
|
cls._meta.add_field(self)
|
||||||
|
@ -45,42 +44,39 @@ class Field(object):
|
||||||
resolve_fn = lambda root, args, info: root.resolve(self.field_name, args, info)
|
resolve_fn = lambda root, args, info: root.resolve(self.field_name, args, info)
|
||||||
return resolve_fn(instance, args, info)
|
return resolve_fn(instance, args, info)
|
||||||
|
|
||||||
def get_object_type(self):
|
def get_object_type(self, schema):
|
||||||
field_type = self.field_type
|
field_type = self.field_type
|
||||||
_is_class = inspect.isclass(field_type)
|
_is_class = inspect.isclass(field_type)
|
||||||
if isinstance(field_type, Field):
|
if isinstance(field_type, Field):
|
||||||
return field_type.get_object_type()
|
return field_type.get_object_type(schema)
|
||||||
if _is_class and issubclass(field_type, ObjectType):
|
if _is_class and issubclass(field_type, BaseObjectType):
|
||||||
return field_type
|
return field_type
|
||||||
elif isinstance(field_type, basestring):
|
elif isinstance(field_type, basestring):
|
||||||
if field_type == 'self':
|
if field_type == 'self':
|
||||||
return self.object_type
|
return self.object_type
|
||||||
elif self.schema:
|
else:
|
||||||
return self.schema.get_type(field_type)
|
return schema.get_type(field_type)
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def type(self):
|
|
||||||
field_type = self.field_type
|
|
||||||
if isinstance(field_type, Field):
|
|
||||||
field_type = self.field_type.type
|
|
||||||
else:
|
|
||||||
object_type = self.get_object_type()
|
|
||||||
if object_type:
|
|
||||||
field_type = object_type._meta.type
|
|
||||||
|
|
||||||
field_type = self.type_wrapper(field_type)
|
|
||||||
return field_type
|
|
||||||
|
|
||||||
def type_wrapper(self, field_type):
|
def type_wrapper(self, field_type):
|
||||||
if not self.null:
|
if not self.null:
|
||||||
field_type = GraphQLNonNull(field_type)
|
field_type = GraphQLNonNull(field_type)
|
||||||
return field_type
|
return field_type
|
||||||
|
|
||||||
@cached_property
|
@memoize
|
||||||
def field(self):
|
def internal_type(self, schema):
|
||||||
# if not self.field_type:
|
field_type = self.field_type
|
||||||
# raise Exception('Must specify a field GraphQL type for the field %s'%self.field_name)
|
if isinstance(field_type, Field):
|
||||||
|
field_type = self.field_type.internal_type(schema)
|
||||||
|
else:
|
||||||
|
object_type = self.get_object_type(schema)
|
||||||
|
if object_type:
|
||||||
|
field_type = object_type.internal_type(schema)
|
||||||
|
|
||||||
|
field_type = self.type_wrapper(field_type)
|
||||||
|
return field_type
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
def internal_field(self, schema):
|
||||||
if not self.object_type:
|
if not self.object_type:
|
||||||
raise Exception('Field could not be constructed in a non graphene.Type or graphene.Interface')
|
raise Exception('Field could not be constructed in a non graphene.Type or graphene.Interface')
|
||||||
|
|
||||||
|
@ -97,8 +93,10 @@ class Field(object):
|
||||||
','.join(meta_attrs.keys())
|
','.join(meta_attrs.keys())
|
||||||
))
|
))
|
||||||
|
|
||||||
|
internal_type = self.internal_type(schema)
|
||||||
|
|
||||||
return GraphQLField(
|
return GraphQLField(
|
||||||
self.type,
|
internal_type,
|
||||||
description=self.description,
|
description=self.description,
|
||||||
args=self.args,
|
args=self.args,
|
||||||
resolver=self.resolver,
|
resolver=self.resolver,
|
||||||
|
@ -122,30 +120,46 @@ class Field(object):
|
||||||
class NativeField(Field):
|
class NativeField(Field):
|
||||||
def __init__(self, field=None):
|
def __init__(self, field=None):
|
||||||
super(NativeField, self).__init__(None)
|
super(NativeField, self).__init__(None)
|
||||||
self.field = field or getattr(self, 'field')
|
self.field = field
|
||||||
|
|
||||||
|
def get_field(self, schema):
|
||||||
|
return self.field
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
def internal_field(self, schema):
|
||||||
|
return self.get_field(schema)
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
def internal_type(self, schema):
|
||||||
|
return self.internal_field(schema).type
|
||||||
|
|
||||||
|
|
||||||
class LazyField(Field):
|
class LazyField(Field):
|
||||||
@cached_property
|
@memoize
|
||||||
def inner_field(self):
|
def inner_field(self, schema):
|
||||||
return self.get_field()
|
return self.get_field(schema)
|
||||||
|
|
||||||
@cached_property
|
def internal_type(self, schema):
|
||||||
def type(self):
|
return self.inner_field(schema).internal_type(schema)
|
||||||
return self.inner_field.type
|
|
||||||
|
|
||||||
@cached_property
|
def internal_field(self, schema):
|
||||||
def field(self):
|
return self.inner_field(schema).internal_field(schema)
|
||||||
return self.inner_field.field
|
|
||||||
|
|
||||||
|
|
||||||
class LazyNativeField(LazyField):
|
class LazyNativeField(NativeField):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(LazyNativeField, self).__init__(None, *args, **kwargs)
|
super(LazyNativeField, self).__init__(None, *args, **kwargs)
|
||||||
|
|
||||||
@cached_property
|
def get_field(self, schema):
|
||||||
def field(self):
|
raise NotImplementedError("get_field function not implemented for %s LazyField" % self.__class__)
|
||||||
return self.inner_field
|
|
||||||
|
@memoize
|
||||||
|
def internal_field(self, schema):
|
||||||
|
return self.get_field(schema)
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
def internal_type(self, schema):
|
||||||
|
return self.internal_field(schema).type
|
||||||
|
|
||||||
|
|
||||||
class TypeField(Field):
|
class TypeField(Field):
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
from graphene.env import get_global_schema
|
|
||||||
from graphene.utils import cached_property
|
from graphene.utils import cached_property
|
||||||
|
|
||||||
DEFAULT_NAMES = ('description', 'name', 'interface', 'schema',
|
DEFAULT_NAMES = ('description', 'name', 'interface',
|
||||||
'type_name', 'interfaces', 'proxy')
|
'type_name', 'interfaces', 'proxy')
|
||||||
|
|
||||||
|
|
||||||
class Options(object):
|
class Options(object):
|
||||||
def __init__(self, meta=None, schema=None):
|
def __init__(self, meta=None):
|
||||||
self.meta = meta
|
self.meta = meta
|
||||||
self.local_fields = []
|
self.local_fields = []
|
||||||
self.interface = False
|
self.interface = False
|
||||||
self.proxy = False
|
self.proxy = False
|
||||||
self.schema = schema or get_global_schema()
|
|
||||||
self.interfaces = []
|
self.interfaces = []
|
||||||
self.parents = []
|
self.parents = []
|
||||||
self.valid_attrs = DEFAULT_NAMES
|
self.valid_attrs = DEFAULT_NAMES
|
||||||
|
@ -71,7 +69,3 @@ class Options(object):
|
||||||
@cached_property
|
@cached_property
|
||||||
def fields_map(self):
|
def fields_map(self):
|
||||||
return {f.field_name: f for f in self.fields}
|
return {f.field_name: f for f in self.fields}
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def type(self):
|
|
||||||
return self.parent.get_graphql_type()
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
from graphql.core import graphql
|
from graphql.core import graphql
|
||||||
from graphql.core.type import (
|
from graphql.core.type import (
|
||||||
GraphQLSchema
|
GraphQLSchema
|
||||||
|
@ -10,10 +12,10 @@ class Schema(object):
|
||||||
_query = None
|
_query = None
|
||||||
|
|
||||||
def __init__(self, query=None, mutation=None, name='Schema'):
|
def __init__(self, query=None, mutation=None, name='Schema'):
|
||||||
|
self._internal_types = {}
|
||||||
self.mutation = mutation
|
self.mutation = mutation
|
||||||
self.query = query
|
self.query = query
|
||||||
self.name = name
|
self.name = name
|
||||||
self._types = {}
|
|
||||||
signals.init_schema.send(self)
|
signals.init_schema.send(self)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -25,34 +27,33 @@ class Schema(object):
|
||||||
|
|
||||||
@query.setter
|
@query.setter
|
||||||
def query(self, query):
|
def query(self, query):
|
||||||
if not query:
|
|
||||||
return
|
|
||||||
self._query = query
|
self._query = query
|
||||||
self._query_type = query._meta.type
|
self._query_type = query and query.internal_type(self)
|
||||||
self._schema = GraphQLSchema(query=self._query_type, mutation=self.mutation)
|
|
||||||
|
|
||||||
def register_type(self, type):
|
@cached_property
|
||||||
type_name = type._meta.type_name
|
def schema(self):
|
||||||
if type_name in self._types:
|
if not self._query_type:
|
||||||
raise Exception('Type name %s already registered in %r' % (type_name, self))
|
raise Exception('You have to define a base query type')
|
||||||
self._types[type_name] = type
|
return GraphQLSchema(query=self._query_type, mutation=self.mutation)
|
||||||
|
|
||||||
|
def associate_internal_type(self, internal_type, object_type):
|
||||||
|
self._internal_types[internal_type.name] = object_type
|
||||||
|
|
||||||
def get_type(self, type_name):
|
def get_type(self, type_name):
|
||||||
if type_name not in self._types:
|
# print 'get_type'
|
||||||
|
# _type = self.schema.get_type(type_name)
|
||||||
|
if type_name not in self._internal_types:
|
||||||
raise Exception('Type %s not found in %r' % (type_name, self))
|
raise Exception('Type %s not found in %r' % (type_name, self))
|
||||||
return self._types[type_name]
|
return self._internal_types[type_name]
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
return self.get_type(name)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def types(self):
|
def types(self):
|
||||||
return self._types
|
return self._internal_types
|
||||||
|
|
||||||
def execute(self, request='', root=None, vars=None, operation_name=None):
|
def execute(self, request='', root=None, vars=None, operation_name=None):
|
||||||
root = root or object()
|
root = root or object()
|
||||||
return graphql(
|
return graphql(
|
||||||
self._schema,
|
self.schema,
|
||||||
request=request,
|
request=request,
|
||||||
root=self.query(root),
|
root=self.query(root),
|
||||||
vars=vars,
|
vars=vars,
|
||||||
|
@ -62,9 +63,12 @@ class Schema(object):
|
||||||
def introspect(self):
|
def introspect(self):
|
||||||
return self._schema.get_type_map()
|
return self._schema.get_type_map()
|
||||||
|
|
||||||
|
def register_internal_type(fun):
|
||||||
|
@wraps(fun)
|
||||||
|
def wrapper(cls, schema):
|
||||||
|
internal_type = fun(cls, schema)
|
||||||
|
if isinstance(schema, Schema):
|
||||||
|
schema.associate_internal_type(internal_type, cls)
|
||||||
|
return internal_type
|
||||||
|
|
||||||
@signals.class_prepared.connect
|
return wrapper
|
||||||
def object_type_created(object_type):
|
|
||||||
schema = object_type._meta.schema
|
|
||||||
if schema:
|
|
||||||
schema.register_type(object_type)
|
|
||||||
|
|
|
@ -8,11 +8,15 @@ from graphql.core.type import (
|
||||||
|
|
||||||
from graphene import signals
|
from graphene import signals
|
||||||
from graphene.core.options import Options
|
from graphene.core.options import Options
|
||||||
|
from graphene.utils import memoize
|
||||||
|
from graphene.core.schema import register_internal_type
|
||||||
|
|
||||||
class ObjectTypeMeta(type):
|
class ObjectTypeMeta(type):
|
||||||
options_cls = Options
|
options_cls = Options
|
||||||
|
|
||||||
|
def is_interface(cls, parents):
|
||||||
|
return Interface in parents
|
||||||
|
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
super_new = super(ObjectTypeMeta, cls).__new__
|
super_new = super(ObjectTypeMeta, cls).__new__
|
||||||
parents = [b for b in bases if isinstance(b, cls)]
|
parents = [b for b in bases if isinstance(b, cls)]
|
||||||
|
@ -27,7 +31,6 @@ class ObjectTypeMeta(type):
|
||||||
'__doc__': doc
|
'__doc__': doc
|
||||||
})
|
})
|
||||||
attr_meta = attrs.pop('Meta', None)
|
attr_meta = attrs.pop('Meta', None)
|
||||||
proxy = None
|
|
||||||
if not attr_meta:
|
if not attr_meta:
|
||||||
meta = None
|
meta = None
|
||||||
# meta = getattr(new_class, 'Meta', None)
|
# meta = getattr(new_class, 'Meta', None)
|
||||||
|
@ -36,13 +39,9 @@ class ObjectTypeMeta(type):
|
||||||
|
|
||||||
base_meta = getattr(new_class, '_meta', None)
|
base_meta = getattr(new_class, '_meta', None)
|
||||||
|
|
||||||
schema = (base_meta and base_meta.schema)
|
new_class.add_to_class('_meta', new_class.options_cls(meta))
|
||||||
|
|
||||||
new_class.add_to_class('_meta', new_class.options_cls(meta, schema))
|
new_class._meta.interface = new_class.is_interface(parents)
|
||||||
|
|
||||||
if base_meta and base_meta.proxy:
|
|
||||||
new_class._meta.interface = base_meta.interface
|
|
||||||
|
|
||||||
# Add all attributes to the class.
|
# Add all attributes to the class.
|
||||||
for obj_name, obj in attrs.items():
|
for obj_name, obj in attrs.items():
|
||||||
new_class.add_to_class(obj_name, obj)
|
new_class.add_to_class(obj_name, obj)
|
||||||
|
@ -93,13 +92,13 @@ class ObjectTypeMeta(type):
|
||||||
setattr(cls, name, value)
|
setattr(cls, name, value)
|
||||||
|
|
||||||
|
|
||||||
class ObjectType(six.with_metaclass(ObjectTypeMeta)):
|
class BaseObjectType(object):
|
||||||
def __new__(cls, instance=None, *args, **kwargs):
|
def __new__(cls, instance=None, *args, **kwargs):
|
||||||
if cls._meta.interface:
|
if cls._meta.interface:
|
||||||
raise Exception("An interface cannot be initialized")
|
raise Exception("An interface cannot be initialized")
|
||||||
if instance == None:
|
if instance == None:
|
||||||
return None
|
return None
|
||||||
return super(ObjectType, cls).__new__(cls, instance, *args, **kwargs)
|
return super(BaseObjectType, cls).__new__(cls, instance, *args, **kwargs)
|
||||||
|
|
||||||
def __init__(self, instance=None):
|
def __init__(self, instance=None):
|
||||||
signals.pre_init.send(self.__class__, instance=instance)
|
signals.pre_init.send(self.__class__, instance=instance)
|
||||||
|
@ -128,28 +127,35 @@ class ObjectType(six.with_metaclass(ObjectTypeMeta)):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def resolve_type(cls, instance, *_):
|
def resolve_type(cls, schema, instance, *_):
|
||||||
return instance._meta.type
|
return instance.internal_type(schema)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_graphql_type(cls):
|
@memoize
|
||||||
fields = cls._meta.fields_map
|
@register_internal_type
|
||||||
|
def internal_type(cls, schema):
|
||||||
|
fields_map = cls._meta.fields_map
|
||||||
|
fields = lambda: {
|
||||||
|
name: field.internal_field(schema)
|
||||||
|
for name, field in fields_map.items()
|
||||||
|
}
|
||||||
if cls._meta.interface:
|
if cls._meta.interface:
|
||||||
return GraphQLInterfaceType(
|
return GraphQLInterfaceType(
|
||||||
cls._meta.type_name,
|
cls._meta.type_name,
|
||||||
description=cls._meta.description,
|
description=cls._meta.description,
|
||||||
resolve_type=cls.resolve_type,
|
resolve_type=lambda *args, **kwargs: cls.resolve_type(schema, *args, **kwargs),
|
||||||
fields=lambda: {name: field.field for name, field in fields.items()}
|
fields=fields
|
||||||
)
|
)
|
||||||
return GraphQLObjectType(
|
return GraphQLObjectType(
|
||||||
cls._meta.type_name,
|
cls._meta.type_name,
|
||||||
description=cls._meta.description,
|
description=cls._meta.description,
|
||||||
interfaces=[i._meta.type for i in cls._meta.interfaces],
|
interfaces=[i.internal_type(schema) for i in cls._meta.interfaces],
|
||||||
fields=lambda: {name: field.field for name, field in fields.items()}
|
fields=fields
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Interface(ObjectType):
|
class ObjectType(six.with_metaclass(ObjectTypeMeta, BaseObjectType)):
|
||||||
class Meta:
|
pass
|
||||||
interface = True
|
|
||||||
proxy = True
|
class Interface(six.with_metaclass(ObjectTypeMeta, BaseObjectType)):
|
||||||
|
pass
|
||||||
|
|
|
@ -5,6 +5,6 @@ from graphene.relay.fields import (
|
||||||
|
|
||||||
import graphene.relay.connections
|
import graphene.relay.connections
|
||||||
|
|
||||||
from graphene.relay.nodes import (
|
from graphene.relay.types import (
|
||||||
Node
|
Node
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,24 +1,15 @@
|
||||||
from graphql_relay.node.node import (
|
from graphql_relay.node.node import (
|
||||||
globalIdField
|
globalIdField
|
||||||
)
|
)
|
||||||
from graphql_relay.connection.connection import (
|
|
||||||
connectionDefinitions
|
|
||||||
)
|
|
||||||
|
|
||||||
from graphene import signals
|
from graphene import signals
|
||||||
from graphene.core.fields import NativeField
|
from graphene.relay.fields import NodeIDField
|
||||||
|
from graphene.relay.types import BaseNode, Node
|
||||||
|
|
||||||
@signals.class_prepared.connect
|
@signals.class_prepared.connect
|
||||||
def object_type_created(object_type):
|
def object_type_created(object_type):
|
||||||
schema = object_type._meta.schema
|
if issubclass(object_type, BaseNode) and BaseNode not in object_type.__bases__:
|
||||||
if hasattr(schema, 'Node') and issubclass(object_type, schema.Node) and object_type != schema.Node:
|
|
||||||
if object_type._meta.proxy:
|
|
||||||
return
|
|
||||||
type_name = object_type._meta.type_name
|
type_name = object_type._meta.type_name
|
||||||
field = NativeField(globalIdField(type_name))
|
field = NodeIDField()
|
||||||
object_type.add_to_class('id', field)
|
object_type.add_to_class('id', field)
|
||||||
assert hasattr(object_type, 'get_node'), 'get_node classmethod not found in %s Node' % type_name
|
assert hasattr(object_type, 'get_node'), 'get_node classmethod not found in %s Node' % type_name
|
||||||
|
|
||||||
connection = connectionDefinitions(type_name, object_type._meta.type).connectionType
|
|
||||||
object_type.add_to_class('connection', connection)
|
|
||||||
|
|
|
@ -6,8 +6,13 @@ from graphql_relay.connection.arrayconnection import (
|
||||||
from graphql_relay.connection.connection import (
|
from graphql_relay.connection.connection import (
|
||||||
connectionArgs
|
connectionArgs
|
||||||
)
|
)
|
||||||
|
from graphql_relay.node.node import (
|
||||||
|
globalIdField
|
||||||
|
)
|
||||||
|
|
||||||
from graphene.core.fields import Field, LazyNativeField
|
from graphene.core.fields import Field, LazyNativeField
|
||||||
from graphene.utils import cached_property
|
from graphene.utils import cached_property
|
||||||
|
from graphene.utils import memoize
|
||||||
|
|
||||||
|
|
||||||
class ConnectionField(Field):
|
class ConnectionField(Field):
|
||||||
|
@ -25,13 +30,20 @@ class ConnectionField(Field):
|
||||||
assert isinstance(resolved, collections.Iterable), 'Resolved value from the connection field have to be iterable'
|
assert isinstance(resolved, collections.Iterable), 'Resolved value from the connection field have to be iterable'
|
||||||
return connectionFromArray(resolved, args)
|
return connectionFromArray(resolved, args)
|
||||||
|
|
||||||
@cached_property
|
@memoize
|
||||||
def type(self):
|
def internal_type(self, schema):
|
||||||
object_type = self.get_object_type()
|
from graphene.relay.types import BaseNode
|
||||||
assert issubclass(object_type, self.schema.Node), 'Only nodes have connections.'
|
object_type = self.get_object_type(schema)
|
||||||
return object_type.connection
|
assert issubclass(object_type, BaseNode), 'Only nodes have connections.'
|
||||||
|
return object_type.get_connection(schema)
|
||||||
|
|
||||||
|
|
||||||
class NodeField(LazyNativeField):
|
class NodeField(LazyNativeField):
|
||||||
def get_field(self):
|
def get_field(self, schema):
|
||||||
return self.schema.Node._definitions.nodeField
|
from graphene.relay.types import BaseNode
|
||||||
|
return BaseNode.get_definitions(schema).nodeField
|
||||||
|
|
||||||
|
|
||||||
|
class NodeIDField(LazyNativeField):
|
||||||
|
def get_field(self, schema):
|
||||||
|
return globalIdField(self.object_type._meta.type_name)
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
from graphql_relay.node.node import (
|
|
||||||
nodeDefinitions,
|
|
||||||
fromGlobalId
|
|
||||||
)
|
|
||||||
from graphene.env import get_global_schema
|
|
||||||
from graphene.core.types import Interface
|
|
||||||
from graphene.core.fields import LazyNativeField
|
|
||||||
|
|
||||||
|
|
||||||
def get_node_type(obj):
|
|
||||||
return obj._meta.type
|
|
||||||
|
|
||||||
|
|
||||||
def get_node(schema, globalId, *args):
|
|
||||||
resolvedGlobalId = fromGlobalId(globalId)
|
|
||||||
_type, _id = resolvedGlobalId.type, resolvedGlobalId.id
|
|
||||||
object_type = schema.get_type(_type)
|
|
||||||
return object_type.get_node(_id)
|
|
||||||
|
|
||||||
class Node(Interface):
|
|
||||||
_definitions = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def contribute_to_schema(cls, schema):
|
|
||||||
if cls._definitions:
|
|
||||||
return
|
|
||||||
schema = cls._meta.schema
|
|
||||||
cls._definitions = nodeDefinitions(lambda *args: get_node(schema, *args), get_node_type)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_graphql_type(cls):
|
|
||||||
if cls is cls._meta.schema.Node:
|
|
||||||
# Return only nodeInterface when is the Node Inerface
|
|
||||||
cls.contribute_to_schema(cls._meta.schema)
|
|
||||||
return cls._definitions.nodeInterface
|
|
||||||
return super(Node, cls).get_graphql_type()
|
|
||||||
|
|
49
graphene/relay/types.py
Normal file
49
graphene/relay/types.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
from graphql_relay.node.node import (
|
||||||
|
nodeDefinitions,
|
||||||
|
fromGlobalId
|
||||||
|
)
|
||||||
|
from graphql_relay.connection.connection import (
|
||||||
|
connectionDefinitions
|
||||||
|
)
|
||||||
|
|
||||||
|
from graphene.env import get_global_schema
|
||||||
|
from graphene.core.types import Interface
|
||||||
|
from graphene.core.fields import LazyNativeField
|
||||||
|
from graphene.utils import memoize
|
||||||
|
|
||||||
|
|
||||||
|
def get_node_type(schema, obj):
|
||||||
|
return obj.internal_type(schema)
|
||||||
|
|
||||||
|
|
||||||
|
def get_node(schema, globalId, *args):
|
||||||
|
resolvedGlobalId = fromGlobalId(globalId)
|
||||||
|
_type, _id = resolvedGlobalId.type, resolvedGlobalId.id
|
||||||
|
object_type = schema.get_type(_type)
|
||||||
|
return object_type.get_node(_id)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseNode(object):
|
||||||
|
@classmethod
|
||||||
|
@memoize
|
||||||
|
def get_definitions(cls, schema):
|
||||||
|
return nodeDefinitions(lambda *args: get_node(schema, *args), lambda *args: get_node_type(schema, *args))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@memoize
|
||||||
|
def get_connection(cls, schema):
|
||||||
|
_type = cls.internal_type(schema)
|
||||||
|
type_name = cls._meta.type_name
|
||||||
|
connection = connectionDefinitions(type_name, _type).connectionType
|
||||||
|
return connection
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def internal_type(cls, schema):
|
||||||
|
if cls is Node or BaseNode in cls.__bases__:
|
||||||
|
# Return only nodeInterface when is the Node Inerface
|
||||||
|
return BaseNode.get_definitions(schema).nodeInterface
|
||||||
|
return super(BaseNode, cls).internal_type(schema)
|
||||||
|
|
||||||
|
|
||||||
|
class Node(BaseNode, Interface):
|
||||||
|
pass
|
|
@ -1,3 +1,5 @@
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
class cached_property(object):
|
class cached_property(object):
|
||||||
"""
|
"""
|
||||||
A property that is only computed once per instance and then replaces itself
|
A property that is only computed once per instance and then replaces itself
|
||||||
|
@ -14,3 +16,17 @@ class cached_property(object):
|
||||||
return self
|
return self
|
||||||
value = obj.__dict__[self.func.__name__] = self.func(obj)
|
value = obj.__dict__[self.func.__name__] = self.func(obj)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def memoize(fun):
|
||||||
|
"""A simple memoize decorator for functions supporting positional args."""
|
||||||
|
@wraps(fun)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
key = (args, frozenset(sorted(kwargs.items())))
|
||||||
|
try:
|
||||||
|
return cache[key]
|
||||||
|
except KeyError:
|
||||||
|
ret = cache[key] = fun(*args, **kwargs)
|
||||||
|
return ret
|
||||||
|
cache = {}
|
||||||
|
return wrapper
|
||||||
|
|
|
@ -114,6 +114,7 @@ def test_should_node():
|
||||||
class Query1(graphene.ObjectType):
|
class Query1(graphene.ObjectType):
|
||||||
node = relay.NodeField()
|
node = relay.NodeField()
|
||||||
reporter = graphene.Field(ReporterNodeType)
|
reporter = graphene.Field(ReporterNodeType)
|
||||||
|
article = graphene.Field(ArticleNodeType)
|
||||||
|
|
||||||
def resolve_reporter(self, *args, **kwargs):
|
def resolve_reporter(self, *args, **kwargs):
|
||||||
return ReporterNodeType(Reporter(id=1, first_name='ABA', last_name='X'))
|
return ReporterNodeType(Reporter(id=1, first_name='ABA', last_name='X'))
|
||||||
|
|
65
tests/contrib_django/test_types.py
Normal file
65
tests/contrib_django/test_types.py
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
from py.test import raises
|
||||||
|
from collections import namedtuple
|
||||||
|
from pytest import raises
|
||||||
|
from graphene.core.fields import (
|
||||||
|
Field,
|
||||||
|
StringField,
|
||||||
|
)
|
||||||
|
from graphql.core.type import (
|
||||||
|
GraphQLObjectType,
|
||||||
|
GraphQLInterfaceType
|
||||||
|
)
|
||||||
|
|
||||||
|
from graphene import Schema
|
||||||
|
from graphene.contrib.django.types import (
|
||||||
|
DjangoNode,
|
||||||
|
DjangoInterface
|
||||||
|
)
|
||||||
|
|
||||||
|
from .models import Reporter, Article
|
||||||
|
|
||||||
|
|
||||||
|
class Character(DjangoInterface):
|
||||||
|
'''Character description'''
|
||||||
|
class Meta:
|
||||||
|
model = Reporter
|
||||||
|
|
||||||
|
|
||||||
|
class Human(DjangoNode):
|
||||||
|
'''Human description'''
|
||||||
|
def get_node(self, id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Article
|
||||||
|
|
||||||
|
schema = Schema()
|
||||||
|
|
||||||
|
|
||||||
|
def test_django_interface():
|
||||||
|
assert DjangoNode._meta.interface == True
|
||||||
|
|
||||||
|
def test_pseudo_interface():
|
||||||
|
object_type = Character.internal_type(schema)
|
||||||
|
assert Character._meta.interface == True
|
||||||
|
assert isinstance(object_type, GraphQLInterfaceType)
|
||||||
|
assert Character._meta.model == Reporter
|
||||||
|
assert object_type.get_fields().keys() == ['articles', 'first_name', 'last_name', 'id', 'email']
|
||||||
|
|
||||||
|
|
||||||
|
def test_interface_resolve_type():
|
||||||
|
resolve_type = Character.resolve_type(schema, Human(object()))
|
||||||
|
assert isinstance(resolve_type, GraphQLObjectType)
|
||||||
|
|
||||||
|
|
||||||
|
def test_object_type():
|
||||||
|
object_type = Human.internal_type(schema)
|
||||||
|
assert Human._meta.interface == False
|
||||||
|
assert isinstance(object_type, GraphQLObjectType)
|
||||||
|
assert object_type.get_fields() == {
|
||||||
|
'headline': Human._meta.fields_map['headline'].internal_field(schema),
|
||||||
|
'id': Human._meta.fields_map['id'].internal_field(schema),
|
||||||
|
'reporter': Human._meta.fields_map['reporter'].internal_field(schema),
|
||||||
|
'pub_date': Human._meta.fields_map['pub_date'].internal_field(schema),
|
||||||
|
}
|
||||||
|
assert object_type.get_interfaces() == [DjangoNode.internal_type(schema)]
|
|
@ -28,34 +28,65 @@ ot = ObjectType()
|
||||||
|
|
||||||
ObjectType._meta.contribute_to_class(ObjectType, '_meta')
|
ObjectType._meta.contribute_to_class(ObjectType, '_meta')
|
||||||
|
|
||||||
|
class Schema(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
schema = Schema()
|
||||||
|
|
||||||
def test_field_no_contributed_raises_error():
|
def test_field_no_contributed_raises_error():
|
||||||
f = Field(GraphQLString)
|
f = Field(GraphQLString)
|
||||||
with raises(Exception) as excinfo:
|
with raises(Exception) as excinfo:
|
||||||
f.field
|
f.internal_field(schema)
|
||||||
|
|
||||||
|
|
||||||
def test_field_type():
|
def test_field_type():
|
||||||
f = Field(GraphQLString)
|
f = Field(GraphQLString)
|
||||||
f.contribute_to_class(ot, 'field_name')
|
f.contribute_to_class(ot, 'field_name')
|
||||||
assert isinstance(f.field, GraphQLField)
|
assert isinstance(f.internal_field(schema), GraphQLField)
|
||||||
assert f.type == GraphQLString
|
assert f.internal_type(schema) == GraphQLString
|
||||||
|
|
||||||
|
|
||||||
def test_stringfield_type():
|
def test_stringfield_type():
|
||||||
f = StringField()
|
f = StringField()
|
||||||
f.contribute_to_class(ot, 'field_name')
|
f.contribute_to_class(ot, 'field_name')
|
||||||
assert f.type == GraphQLString
|
assert f.internal_type(schema) == GraphQLString
|
||||||
|
|
||||||
|
|
||||||
def test_stringfield_type_null():
|
def test_stringfield_type_null():
|
||||||
f = StringField(null=False)
|
f = StringField(null=False)
|
||||||
f.contribute_to_class(ot, 'field_name')
|
f.contribute_to_class(ot, 'field_name')
|
||||||
assert isinstance(f.field, GraphQLField)
|
assert isinstance(f.internal_field(schema), GraphQLField)
|
||||||
assert isinstance(f.type, GraphQLNonNull)
|
assert isinstance(f.internal_type(schema), GraphQLNonNull)
|
||||||
|
|
||||||
|
|
||||||
def test_field_resolve():
|
def test_field_resolve():
|
||||||
f = StringField(null=False)
|
f = StringField(null=False, resolve=lambda *args:'RESOLVED')
|
||||||
f.contribute_to_class(ot, 'field_name')
|
f.contribute_to_class(ot, 'field_name')
|
||||||
field_type = f.field
|
field_type = f.internal_field(schema)
|
||||||
field_type.resolver(ot,2,3)
|
assert 'RESOLVED' == field_type.resolver(ot,2,3)
|
||||||
|
|
||||||
|
|
||||||
|
def test_field_resolve_type_custom():
|
||||||
|
class MyCustomType(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Schema(object):
|
||||||
|
def get_type(self, name):
|
||||||
|
if name == 'MyCustomType':
|
||||||
|
return MyCustomType
|
||||||
|
|
||||||
|
s = Schema()
|
||||||
|
|
||||||
|
f = Field('MyCustomType')
|
||||||
|
f.contribute_to_class(ot, 'field_name')
|
||||||
|
field_type = f.get_object_type(s)
|
||||||
|
assert field_type == MyCustomType
|
||||||
|
|
||||||
|
|
||||||
|
def test_field_resolve_type_custom():
|
||||||
|
s = Schema()
|
||||||
|
|
||||||
|
f = Field('self')
|
||||||
|
f.contribute_to_class(ot, 'field_name')
|
||||||
|
field_type = f.get_object_type(s)
|
||||||
|
assert field_type == ot
|
||||||
|
|
68
tests/core/test_query.py
Normal file
68
tests/core/test_query.py
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
from py.test import raises
|
||||||
|
from collections import namedtuple
|
||||||
|
from pytest import raises
|
||||||
|
from graphql.core import graphql
|
||||||
|
from graphene.core.fields import (
|
||||||
|
Field,
|
||||||
|
StringField,
|
||||||
|
ListField,
|
||||||
|
)
|
||||||
|
from graphql.core.type import (
|
||||||
|
GraphQLObjectType,
|
||||||
|
GraphQLSchema,
|
||||||
|
GraphQLInterfaceType
|
||||||
|
)
|
||||||
|
|
||||||
|
from graphene.core.types import (
|
||||||
|
Interface,
|
||||||
|
ObjectType
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Character(Interface):
|
||||||
|
name = StringField()
|
||||||
|
|
||||||
|
|
||||||
|
class Pet(ObjectType):
|
||||||
|
type = StringField(resolve=lambda *_:'Dog')
|
||||||
|
|
||||||
|
|
||||||
|
class Human(Character):
|
||||||
|
friends = ListField(Character)
|
||||||
|
pet = Field(Pet)
|
||||||
|
|
||||||
|
def resolve_name(self, *args):
|
||||||
|
return 'Peter'
|
||||||
|
|
||||||
|
def resolve_friend(self, *args):
|
||||||
|
return Human(object())
|
||||||
|
|
||||||
|
def resolve_pet(self, *args):
|
||||||
|
return Pet(object())
|
||||||
|
# def resolve_friends(self, *args, **kwargs):
|
||||||
|
# return 'HEY YOU!'
|
||||||
|
|
||||||
|
schema = object()
|
||||||
|
|
||||||
|
Human_type = Human.internal_type(schema)
|
||||||
|
|
||||||
|
|
||||||
|
def test_query():
|
||||||
|
schema = GraphQLSchema(query=Human_type)
|
||||||
|
query = '''
|
||||||
|
{
|
||||||
|
name
|
||||||
|
pet {
|
||||||
|
type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
expected = {
|
||||||
|
'name': 'Peter',
|
||||||
|
'pet': {
|
||||||
|
'type':'Dog'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = graphql(schema, query, root=Human(object()))
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == expected
|
103
tests/core/test_schema.py
Normal file
103
tests/core/test_schema.py
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
from py.test import raises
|
||||||
|
from collections import namedtuple
|
||||||
|
from pytest import raises
|
||||||
|
from graphql.core import graphql
|
||||||
|
from graphene.core.fields import (
|
||||||
|
Field,
|
||||||
|
StringField,
|
||||||
|
ListField,
|
||||||
|
)
|
||||||
|
from graphql.core.type import (
|
||||||
|
GraphQLObjectType,
|
||||||
|
GraphQLSchema,
|
||||||
|
GraphQLInterfaceType
|
||||||
|
)
|
||||||
|
|
||||||
|
from graphene import (
|
||||||
|
Interface,
|
||||||
|
ObjectType,
|
||||||
|
Schema
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
schema = Schema(name='My own schema')
|
||||||
|
|
||||||
|
|
||||||
|
class Character(Interface):
|
||||||
|
name = StringField()
|
||||||
|
|
||||||
|
|
||||||
|
class Pet(ObjectType):
|
||||||
|
type = StringField(resolve=lambda *_:'Dog')
|
||||||
|
|
||||||
|
|
||||||
|
class Human(Character):
|
||||||
|
friends = ListField(Character)
|
||||||
|
pet = Field(Pet)
|
||||||
|
|
||||||
|
def resolve_name(self, *args):
|
||||||
|
return 'Peter'
|
||||||
|
|
||||||
|
def resolve_friend(self, *args):
|
||||||
|
return Human(object())
|
||||||
|
|
||||||
|
def resolve_pet(self, *args):
|
||||||
|
return Pet(object())
|
||||||
|
|
||||||
|
schema.query = Human
|
||||||
|
|
||||||
|
def test_get_registered_type():
|
||||||
|
assert schema.get_type('Character') == Character
|
||||||
|
|
||||||
|
def test_get_unregistered_type():
|
||||||
|
with raises(Exception) as excinfo:
|
||||||
|
schema.get_type('NON_EXISTENT_MODEL')
|
||||||
|
assert 'not found' in str(excinfo.value)
|
||||||
|
|
||||||
|
def test_schema_query():
|
||||||
|
assert schema.query == Human
|
||||||
|
|
||||||
|
def test_query_schema_graphql():
|
||||||
|
a = object()
|
||||||
|
query = '''
|
||||||
|
{
|
||||||
|
name
|
||||||
|
pet {
|
||||||
|
type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
expected = {
|
||||||
|
'name': 'Peter',
|
||||||
|
'pet': {
|
||||||
|
'type':'Dog'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = graphql(schema.schema, query, root=Human(object()))
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_query_schema_execute():
|
||||||
|
a = object()
|
||||||
|
query = '''
|
||||||
|
{
|
||||||
|
name
|
||||||
|
pet {
|
||||||
|
type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
expected = {
|
||||||
|
'name': 'Peter',
|
||||||
|
'pet': {
|
||||||
|
'type':'Dog'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = schema.execute(query, root=object())
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_schema_get_type_map():
|
||||||
|
assert schema.schema.get_type_map().keys() == ['__Field', 'String', 'Pet', 'Character', '__InputValue', '__Directive', '__TypeKind', '__Schema', '__Type', 'Human', '__EnumValue', 'Boolean']
|
|
@ -15,31 +15,43 @@ from graphene.core.types import (
|
||||||
ObjectType
|
ObjectType
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Character(Interface):
|
class Character(Interface):
|
||||||
'''Character description'''
|
'''Character description'''
|
||||||
name = StringField()
|
name = StringField()
|
||||||
class Meta:
|
class Meta:
|
||||||
type_name = 'core.Character'
|
type_name = 'core.Character'
|
||||||
|
|
||||||
|
|
||||||
class Human(Character):
|
class Human(Character):
|
||||||
'''Human description'''
|
'''Human description'''
|
||||||
friends = StringField()
|
friends = StringField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
type_name = 'core.Human'
|
type_name = 'core.Human'
|
||||||
|
|
||||||
|
schema = object()
|
||||||
|
|
||||||
|
|
||||||
def test_interface():
|
def test_interface():
|
||||||
object_type = Character._meta.type
|
object_type = Character.internal_type(schema)
|
||||||
assert Character._meta.interface == True
|
assert Character._meta.interface == True
|
||||||
assert Character._meta.type_name == 'core.Character'
|
|
||||||
assert isinstance(object_type, GraphQLInterfaceType)
|
assert isinstance(object_type, GraphQLInterfaceType)
|
||||||
|
assert Character._meta.type_name == 'core.Character'
|
||||||
assert object_type.description == 'Character description'
|
assert object_type.description == 'Character description'
|
||||||
assert object_type.get_fields() == {'name': Character._meta.fields_map['name'].field}
|
assert object_type.get_fields() == {'name': Character._meta.fields_map['name'].internal_field(schema)}
|
||||||
|
|
||||||
|
|
||||||
|
def test_interface_resolve_type():
|
||||||
|
resolve_type = Character.resolve_type(schema, Human(object()))
|
||||||
|
assert isinstance(resolve_type, GraphQLObjectType)
|
||||||
|
|
||||||
|
|
||||||
def test_object_type():
|
def test_object_type():
|
||||||
object_type = Human._meta.type
|
object_type = Human.internal_type(schema)
|
||||||
assert Human._meta.interface == False
|
assert Human._meta.interface == False
|
||||||
assert Human._meta.type_name == 'core.Human'
|
assert Human._meta.type_name == 'core.Human'
|
||||||
assert isinstance(object_type, GraphQLObjectType)
|
assert isinstance(object_type, GraphQLObjectType)
|
||||||
assert object_type.description == 'Human description'
|
assert object_type.description == 'Human description'
|
||||||
assert object_type.get_fields() == {'name': Character._meta.fields_map['name'].field, 'friends': Human._meta.fields_map['friends'].field}
|
assert object_type.get_fields() == {'name': Character._meta.fields_map['name'].internal_field(schema), 'friends': Human._meta.fields_map['friends'].internal_field(schema)}
|
||||||
assert object_type.get_interfaces() == [Character._meta.type]
|
assert object_type.get_interfaces() == [Character.internal_type(schema)]
|
||||||
|
|
|
@ -21,8 +21,12 @@ def test_field_no_contributed_raises_error():
|
||||||
assert 'get_node' in str(excinfo.value)
|
assert 'get_node' in str(excinfo.value)
|
||||||
|
|
||||||
|
|
||||||
def test_node_should_have_connection():
|
def test_node_should_have_same_connection_always():
|
||||||
assert OtherNode.connection
|
s = object()
|
||||||
|
connection1 = OtherNode.get_connection(s)
|
||||||
|
connection2 = OtherNode.get_connection(s)
|
||||||
|
|
||||||
|
assert connection1 == connection2
|
||||||
|
|
||||||
|
|
||||||
def test_node_should_have_id_field():
|
def test_node_should_have_id_field():
|
||||||
|
|
|
@ -88,6 +88,9 @@ def createShip(shipName, factionId):
|
||||||
def getShip(_id):
|
def getShip(_id):
|
||||||
return Ship.objects.get(id=_id)
|
return Ship.objects.get(id=_id)
|
||||||
|
|
||||||
|
def getShips():
|
||||||
|
return Ship.objects.all()
|
||||||
|
|
||||||
def getFaction(_id):
|
def getFaction(_id):
|
||||||
return Faction.objects.get(id=_id)
|
return Faction.objects.get(id=_id)
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ from .models import Ship as ShipModel, Faction as FactionModel
|
||||||
from .data import (
|
from .data import (
|
||||||
getFaction,
|
getFaction,
|
||||||
getShip,
|
getShip,
|
||||||
|
getShips,
|
||||||
getRebels,
|
getRebels,
|
||||||
getEmpire,
|
getEmpire,
|
||||||
)
|
)
|
||||||
|
@ -35,6 +36,11 @@ class Query(graphene.ObjectType):
|
||||||
rebels = graphene.Field(Faction)
|
rebels = graphene.Field(Faction)
|
||||||
empire = graphene.Field(Faction)
|
empire = graphene.Field(Faction)
|
||||||
node = relay.NodeField()
|
node = relay.NodeField()
|
||||||
|
ships = relay.ConnectionField(Ship, description='All the ships.')
|
||||||
|
|
||||||
|
@resolve_only_args
|
||||||
|
def resolve_ships(self):
|
||||||
|
return [Ship(s) for s in getShips()]
|
||||||
|
|
||||||
@resolve_only_args
|
@resolve_only_args
|
||||||
def resolve_rebels(self):
|
def resolve_rebels(self):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user