mirror of
				https://github.com/graphql-python/graphene.git
				synced 2025-11-04 01:47:45 +03:00 
			
		
		
		
	Merge pull request #2 from syrusakbary/django
Django Models integration
This commit is contained in:
		
						commit
						b5f49b1014
					
				| 
						 | 
				
			
			@ -4,6 +4,8 @@ python:
 | 
			
		|||
- 2.7
 | 
			
		||||
install:
 | 
			
		||||
- pip install pytest pytest-cov coveralls flake8 six blinker
 | 
			
		||||
# - 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 graphql-relay
 | 
			
		||||
- python setup.py develop
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,4 +35,4 @@ from graphene.decorators import (
 | 
			
		|||
    resolve_only_args
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
import graphene.relay
 | 
			
		||||
# import graphene.relay
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										0
									
								
								graphene/contrib/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								graphene/contrib/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										4
									
								
								graphene/contrib/django/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								graphene/contrib/django/__init__.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
from graphene.contrib.django.types import (
 | 
			
		||||
	DjangoObjectType,
 | 
			
		||||
	DjangoNode
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										69
									
								
								graphene/contrib/django/converter.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								graphene/contrib/django/converter.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,69 @@
 | 
			
		|||
from singledispatch import singledispatch
 | 
			
		||||
from django.db import models
 | 
			
		||||
 | 
			
		||||
from graphene.core.fields import (
 | 
			
		||||
    StringField,
 | 
			
		||||
    IDField,
 | 
			
		||||
    IntField,
 | 
			
		||||
    BooleanField,
 | 
			
		||||
    FloatField,
 | 
			
		||||
    ListField
 | 
			
		||||
)
 | 
			
		||||
from graphene.contrib.django.fields import ConnectionOrListField, DjangoModelField
 | 
			
		||||
 | 
			
		||||
@singledispatch
 | 
			
		||||
def convert_django_field(field, cls):
 | 
			
		||||
    raise Exception("Don't know how to convert the Django field %s (%s)" % (field, field.__class__))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@convert_django_field.register(models.DateField)
 | 
			
		||||
@convert_django_field.register(models.CharField)
 | 
			
		||||
@convert_django_field.register(models.TextField)
 | 
			
		||||
@convert_django_field.register(models.EmailField)
 | 
			
		||||
@convert_django_field.register(models.SlugField)
 | 
			
		||||
def _(field, cls):
 | 
			
		||||
    return StringField(description=field.description)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@convert_django_field.register(models.AutoField)
 | 
			
		||||
def _(field, cls):
 | 
			
		||||
    return IDField(description=field.description)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@convert_django_field.register(models.PositiveIntegerField)
 | 
			
		||||
@convert_django_field.register(models.PositiveSmallIntegerField)
 | 
			
		||||
@convert_django_field.register(models.SmallIntegerField)
 | 
			
		||||
@convert_django_field.register(models.BigIntegerField)
 | 
			
		||||
@convert_django_field.register(models.URLField)
 | 
			
		||||
@convert_django_field.register(models.UUIDField)
 | 
			
		||||
@convert_django_field.register(models.IntegerField)
 | 
			
		||||
def _(field, cls):
 | 
			
		||||
    return IntField(description=field.description)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@convert_django_field.register(models.BooleanField)
 | 
			
		||||
def _(field, cls):
 | 
			
		||||
    return BooleanField(description=field.description, null=False)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@convert_django_field.register(models.NullBooleanField)
 | 
			
		||||
def _(field, cls):
 | 
			
		||||
    return BooleanField(description=field.description)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@convert_django_field.register(models.FloatField)
 | 
			
		||||
def _(field, cls):
 | 
			
		||||
    return FloatField(description=field.description)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@convert_django_field.register(models.ManyToManyField)
 | 
			
		||||
@convert_django_field.register(models.ManyToOneRel)
 | 
			
		||||
def _(field, cls):
 | 
			
		||||
    model_field = DjangoModelField(field.related_model)
 | 
			
		||||
    return ConnectionOrListField(model_field)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@convert_django_field.register(models.OneToOneField)
 | 
			
		||||
@convert_django_field.register(models.ForeignKey)
 | 
			
		||||
def _(field, cls):
 | 
			
		||||
    return DjangoModelField(field.related_model, description=field.description)
 | 
			
		||||
							
								
								
									
										62
									
								
								graphene/contrib/django/fields.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								graphene/contrib/django/fields.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,62 @@
 | 
			
		|||
from graphene.core.fields import (
 | 
			
		||||
    ListField
 | 
			
		||||
)
 | 
			
		||||
from graphene import relay
 | 
			
		||||
 | 
			
		||||
from graphene.core.fields import Field, LazyField
 | 
			
		||||
from graphene.utils import cached_property, memoize
 | 
			
		||||
from graphene.env import get_global_schema
 | 
			
		||||
 | 
			
		||||
from graphene.relay.types import BaseNode
 | 
			
		||||
 | 
			
		||||
from django.db.models.query import QuerySet
 | 
			
		||||
from django.db.models.manager import Manager
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_type_for_model(schema, model):
 | 
			
		||||
    schema = schema or get_global_schema()
 | 
			
		||||
    types = schema.types.values()
 | 
			
		||||
    for _type in types:
 | 
			
		||||
        type_model = hasattr(_type,'_meta') and getattr(_type._meta, 'model', None)
 | 
			
		||||
        if model == type_model:
 | 
			
		||||
            return _type
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DjangoConnectionField(relay.ConnectionField):
 | 
			
		||||
    def wrap_resolved(self, value, instance, args, info):
 | 
			
		||||
        if isinstance(value, (QuerySet, Manager)):
 | 
			
		||||
            cls = instance.__class__
 | 
			
		||||
            value = [cls(s) for s in value.all()]
 | 
			
		||||
        return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ConnectionOrListField(LazyField):
 | 
			
		||||
    @memoize
 | 
			
		||||
    def get_field(self, schema):
 | 
			
		||||
        model_field = self.field_type
 | 
			
		||||
        field_object_type = model_field.get_object_type(schema)
 | 
			
		||||
        if field_object_type and issubclass(field_object_type, BaseNode):
 | 
			
		||||
            field = DjangoConnectionField(model_field)
 | 
			
		||||
        else:
 | 
			
		||||
            field = ListField(model_field)
 | 
			
		||||
        field.contribute_to_class(self.object_type, self.field_name)
 | 
			
		||||
        return field
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DjangoModelField(Field):
 | 
			
		||||
    def __init__(self, model, *args, **kwargs):
 | 
			
		||||
        super(DjangoModelField, self).__init__(None, *args, **kwargs)
 | 
			
		||||
        self.model = model
 | 
			
		||||
 | 
			
		||||
    @memoize
 | 
			
		||||
    def internal_type(self, schema):
 | 
			
		||||
        _type = self.get_object_type(schema)
 | 
			
		||||
        return _type and _type.internal_type(schema)
 | 
			
		||||
 | 
			
		||||
    def get_object_type(self, schema):
 | 
			
		||||
        _type = get_type_for_model(schema, self.model)
 | 
			
		||||
        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
 | 
			
		||||
            raise Exception("Field %s (%s) model not mapped in current schema" % (self, self.model._meta.object_name))
 | 
			
		||||
        
 | 
			
		||||
        return _type
 | 
			
		||||
							
								
								
									
										24
									
								
								graphene/contrib/django/options.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								graphene/contrib/django/options.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
import inspect
 | 
			
		||||
from django.db import models
 | 
			
		||||
 | 
			
		||||
from graphene.core.options import Options
 | 
			
		||||
 | 
			
		||||
VALID_ATTRS = ('model', 'only_fields')
 | 
			
		||||
 | 
			
		||||
from graphene.relay.types import Node, BaseNode
 | 
			
		||||
 | 
			
		||||
class DjangoOptions(Options):
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        self.model = None
 | 
			
		||||
        super(DjangoOptions, self).__init__(*args, **kwargs)
 | 
			
		||||
        self.valid_attrs += VALID_ATTRS
 | 
			
		||||
        self.only_fields = None
 | 
			
		||||
 | 
			
		||||
    def contribute_to_class(self, cls, name):
 | 
			
		||||
        super(DjangoOptions, self).contribute_to_class(cls, name)
 | 
			
		||||
        if cls.__name__ == 'DjangoNode':
 | 
			
		||||
            return
 | 
			
		||||
        if not self.model:
 | 
			
		||||
            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):
 | 
			
		||||
            raise Exception('Provided model in %s is not a Django model' % cls)
 | 
			
		||||
							
								
								
									
										45
									
								
								graphene/contrib/django/types.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								graphene/contrib/django/types.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,45 @@
 | 
			
		|||
import six
 | 
			
		||||
from django.db import models
 | 
			
		||||
 | 
			
		||||
from graphene.core.types import ObjectTypeMeta, BaseObjectType
 | 
			
		||||
from graphene.contrib.django.options import DjangoOptions
 | 
			
		||||
from graphene.contrib.django.converter import convert_django_field
 | 
			
		||||
 | 
			
		||||
from graphene.relay.types import Node, BaseNode
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_reverse_fields(model):
 | 
			
		||||
    for name, attr in model.__dict__.items():
 | 
			
		||||
        related = getattr(attr, 'related', None)
 | 
			
		||||
        if isinstance(related, models.ManyToOneRel):
 | 
			
		||||
            yield related
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DjangoObjectTypeMeta(ObjectTypeMeta):
 | 
			
		||||
    options_cls = DjangoOptions
 | 
			
		||||
 | 
			
		||||
    def is_interface(cls, parents):
 | 
			
		||||
        return DjangoInterface in parents
 | 
			
		||||
 | 
			
		||||
    def add_extra_fields(cls):
 | 
			
		||||
        if not cls._meta.model:
 | 
			
		||||
            return
 | 
			
		||||
        only_fields = cls._meta.only_fields
 | 
			
		||||
        reverse_fields = tuple(get_reverse_fields(cls._meta.model))
 | 
			
		||||
        for field in cls._meta.model._meta.fields + reverse_fields:
 | 
			
		||||
            if only_fields and field.name not in only_fields:
 | 
			
		||||
                continue
 | 
			
		||||
            converted_field = convert_django_field(field, cls)
 | 
			
		||||
            cls.add_to_class(field.name, converted_field)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DjangoObjectType(six.with_metaclass(DjangoObjectTypeMeta, BaseObjectType)):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DjangoInterface(six.with_metaclass(DjangoObjectTypeMeta, BaseObjectType)):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DjangoNode(BaseNode, DjangoInterface):
 | 
			
		||||
    pass
 | 
			
		||||
| 
						 | 
				
			
			@ -8,8 +8,10 @@ from graphql.core.type import (
 | 
			
		|||
    GraphQLBoolean,
 | 
			
		||||
    GraphQLID,
 | 
			
		||||
    GraphQLArgument,
 | 
			
		||||
    GraphQLFloat,
 | 
			
		||||
)
 | 
			
		||||
from graphene.utils import cached_property
 | 
			
		||||
from graphene.utils import cached_property, memoize
 | 
			
		||||
from graphene.core.types import BaseObjectType
 | 
			
		||||
 | 
			
		||||
class Field(object):
 | 
			
		||||
    def __init__(self, field_type, resolve=None, null=True, args=None, description='', **extra_args):
 | 
			
		||||
| 
						 | 
				
			
			@ -25,7 +27,6 @@ class Field(object):
 | 
			
		|||
    def contribute_to_class(self, cls, name):
 | 
			
		||||
        self.field_name = name
 | 
			
		||||
        self.object_type = cls
 | 
			
		||||
        self.schema = cls._meta.schema
 | 
			
		||||
        if isinstance(self.field_type, Field) and not self.field_type.object_type:
 | 
			
		||||
            self.field_type.contribute_to_class(cls, name)
 | 
			
		||||
        cls._meta.add_field(self)
 | 
			
		||||
| 
						 | 
				
			
			@ -43,41 +44,39 @@ class Field(object):
 | 
			
		|||
            resolve_fn = lambda root, args, info: root.resolve(self.field_name, args, info)
 | 
			
		||||
        return resolve_fn(instance, args, info)
 | 
			
		||||
 | 
			
		||||
    def get_object_type(self):
 | 
			
		||||
        from graphene.core.types import ObjectType
 | 
			
		||||
    def get_object_type(self, schema):
 | 
			
		||||
        field_type = self.field_type
 | 
			
		||||
        _is_class = inspect.isclass(field_type)
 | 
			
		||||
        if _is_class and issubclass(field_type, ObjectType):
 | 
			
		||||
        if isinstance(field_type, Field):
 | 
			
		||||
            return field_type.get_object_type(schema)
 | 
			
		||||
        if _is_class and issubclass(field_type, BaseObjectType):
 | 
			
		||||
            return field_type
 | 
			
		||||
        elif isinstance(field_type, basestring):
 | 
			
		||||
            if field_type == 'self':
 | 
			
		||||
                return self.object_type
 | 
			
		||||
            elif self.schema:
 | 
			
		||||
                return self.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
 | 
			
		||||
            else:
 | 
			
		||||
                return schema.get_type(field_type)
 | 
			
		||||
 | 
			
		||||
    def type_wrapper(self, field_type):
 | 
			
		||||
        if not self.null:
 | 
			
		||||
            field_type = GraphQLNonNull(field_type)
 | 
			
		||||
        return field_type
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def field(self):
 | 
			
		||||
        if not self.field_type:
 | 
			
		||||
            raise Exception('Must specify a field GraphQL type for the field %s'%self.field_name)
 | 
			
		||||
    @memoize
 | 
			
		||||
    def internal_type(self, schema):
 | 
			
		||||
        field_type = self.field_type
 | 
			
		||||
        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:
 | 
			
		||||
            raise Exception('Field could not be constructed in a non graphene.Type or graphene.Interface')
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -94,8 +93,10 @@ class Field(object):
 | 
			
		|||
                ','.join(meta_attrs.keys())
 | 
			
		||||
            ))
 | 
			
		||||
 | 
			
		||||
        internal_type = self.internal_type(schema)
 | 
			
		||||
 | 
			
		||||
        return GraphQLField(
 | 
			
		||||
            self.type,
 | 
			
		||||
            internal_type,
 | 
			
		||||
            description=self.description,
 | 
			
		||||
            args=self.args,
 | 
			
		||||
            resolver=self.resolver,
 | 
			
		||||
| 
						 | 
				
			
			@ -119,7 +120,46 @@ class Field(object):
 | 
			
		|||
class NativeField(Field):
 | 
			
		||||
    def __init__(self, field=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):
 | 
			
		||||
    @memoize
 | 
			
		||||
    def inner_field(self, schema):
 | 
			
		||||
        return self.get_field(schema)
 | 
			
		||||
 | 
			
		||||
    def internal_type(self, schema):
 | 
			
		||||
        return self.inner_field(schema).internal_type(schema)
 | 
			
		||||
 | 
			
		||||
    def internal_field(self, schema):
 | 
			
		||||
        return self.inner_field(schema).internal_field(schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LazyNativeField(NativeField):
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super(LazyNativeField, self).__init__(None, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def get_field(self, schema):
 | 
			
		||||
        raise NotImplementedError("get_field function not implemented for %s LazyField" % self.__class__)
 | 
			
		||||
 | 
			
		||||
    @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):
 | 
			
		||||
| 
						 | 
				
			
			@ -143,6 +183,10 @@ class IDField(TypeField):
 | 
			
		|||
    field_type = GraphQLID
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FloatField(TypeField):
 | 
			
		||||
    field_type = GraphQLFloat
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ListField(Field):
 | 
			
		||||
    def type_wrapper(self, field_type):
 | 
			
		||||
        return GraphQLList(field_type)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,20 +1,19 @@
 | 
			
		|||
from graphene.env import get_global_schema
 | 
			
		||||
from graphene.utils import cached_property
 | 
			
		||||
 | 
			
		||||
DEFAULT_NAMES = ('description', 'name', 'interface', 'schema',
 | 
			
		||||
                 'type_name', 'interfaces',  'proxy')
 | 
			
		||||
DEFAULT_NAMES = ('description', 'name', 'interface',
 | 
			
		||||
                 'type_name', 'interfaces', 'proxy')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Options(object):
 | 
			
		||||
    def __init__(self, meta=None, schema=None):
 | 
			
		||||
    def __init__(self, meta=None):
 | 
			
		||||
        self.meta = meta
 | 
			
		||||
        self.local_fields = []
 | 
			
		||||
        self.interface = False
 | 
			
		||||
        self.proxy = False
 | 
			
		||||
        self.schema = schema
 | 
			
		||||
        self.interfaces = []
 | 
			
		||||
        self.parents = []
 | 
			
		||||
 | 
			
		||||
        self.valid_attrs = DEFAULT_NAMES
 | 
			
		||||
    
 | 
			
		||||
    def contribute_to_class(self, cls, name):
 | 
			
		||||
        cls._meta = self
 | 
			
		||||
        self.parent = cls
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +35,7 @@ class Options(object):
 | 
			
		|||
                # over it, so we loop over the *original* dictionary instead.
 | 
			
		||||
                if name.startswith('_'):
 | 
			
		||||
                    del meta_attrs[name]
 | 
			
		||||
            for attr_name in DEFAULT_NAMES:
 | 
			
		||||
            for attr_name in self.valid_attrs:
 | 
			
		||||
                if attr_name in meta_attrs:
 | 
			
		||||
                    setattr(self, attr_name, meta_attrs.pop(attr_name))
 | 
			
		||||
                    self.original_attrs[attr_name] = getattr(self, attr_name)
 | 
			
		||||
| 
						 | 
				
			
			@ -44,9 +43,13 @@ class Options(object):
 | 
			
		|||
                    setattr(self, attr_name, getattr(self.meta, attr_name))
 | 
			
		||||
                    self.original_attrs[attr_name] = getattr(self, attr_name)
 | 
			
		||||
 | 
			
		||||
            del self.valid_attrs
 | 
			
		||||
 | 
			
		||||
            # Any leftover attributes must be invalid.
 | 
			
		||||
            if meta_attrs != {}:
 | 
			
		||||
                raise TypeError("'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs.keys()))
 | 
			
		||||
        else:
 | 
			
		||||
            self.proxy = False
 | 
			
		||||
 | 
			
		||||
        if self.interfaces != [] and self.interface:
 | 
			
		||||
            raise Exception("A interface cannot inherit from interfaces")
 | 
			
		||||
| 
						 | 
				
			
			@ -66,7 +69,3 @@ class Options(object):
 | 
			
		|||
    @cached_property
 | 
			
		||||
    def fields_map(self):
 | 
			
		||||
        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.type import (
 | 
			
		||||
    GraphQLSchema
 | 
			
		||||
| 
						 | 
				
			
			@ -10,10 +12,10 @@ class Schema(object):
 | 
			
		|||
    _query = None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, query=None, mutation=None, name='Schema'):
 | 
			
		||||
        self._internal_types = {}
 | 
			
		||||
        self.mutation = mutation
 | 
			
		||||
        self.query = query
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self._types = {}
 | 
			
		||||
        signals.init_schema.send(self)
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -25,27 +27,33 @@ class Schema(object):
 | 
			
		|||
 | 
			
		||||
    @query.setter
 | 
			
		||||
    def query(self, query):
 | 
			
		||||
        if not query:
 | 
			
		||||
            return
 | 
			
		||||
        self._query = query
 | 
			
		||||
        self._query_type = query._meta.type
 | 
			
		||||
        self._schema = GraphQLSchema(query=self._query_type, mutation=self.mutation)
 | 
			
		||||
        self._query_type = query and query.internal_type(self)
 | 
			
		||||
 | 
			
		||||
    def register_type(self, type):
 | 
			
		||||
        type_name = type._meta.type_name
 | 
			
		||||
        if type_name in self._types:
 | 
			
		||||
            raise Exception('Type name %s already registered in %r' % (type_name, self))
 | 
			
		||||
        self._types[type_name] = type
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def schema(self):
 | 
			
		||||
        if not self._query_type:
 | 
			
		||||
            raise Exception('You have to define a base query 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):
 | 
			
		||||
        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))
 | 
			
		||||
        return self._types[type_name]
 | 
			
		||||
        return self._internal_types[type_name]
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def types(self):
 | 
			
		||||
        return self._internal_types
 | 
			
		||||
    
 | 
			
		||||
    def execute(self, request='', root=None, vars=None, operation_name=None):
 | 
			
		||||
        root = root or object()
 | 
			
		||||
        return graphql(
 | 
			
		||||
            self._schema,
 | 
			
		||||
            self.schema,
 | 
			
		||||
            request=request,
 | 
			
		||||
            root=self.query(root),
 | 
			
		||||
            vars=vars,
 | 
			
		||||
| 
						 | 
				
			
			@ -55,9 +63,12 @@ class Schema(object):
 | 
			
		|||
    def introspect(self):
 | 
			
		||||
        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
 | 
			
		||||
def object_type_created(object_type):
 | 
			
		||||
    schema = object_type._meta.schema
 | 
			
		||||
    if schema:
 | 
			
		||||
        schema.register_type(object_type)
 | 
			
		||||
    return wrapper
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,12 +8,18 @@ from graphql.core.type import (
 | 
			
		|||
 | 
			
		||||
from graphene import signals
 | 
			
		||||
from graphene.core.options import Options
 | 
			
		||||
 | 
			
		||||
from graphene.utils import memoize
 | 
			
		||||
from graphene.core.schema import register_internal_type
 | 
			
		||||
 | 
			
		||||
class ObjectTypeMeta(type):
 | 
			
		||||
    options_cls = Options
 | 
			
		||||
 | 
			
		||||
    def is_interface(cls, parents):
 | 
			
		||||
        return Interface in parents
 | 
			
		||||
 | 
			
		||||
    def __new__(cls, name, bases, attrs):
 | 
			
		||||
        super_new = super(ObjectTypeMeta, cls).__new__
 | 
			
		||||
        parents = [b for b in bases if isinstance(b, ObjectTypeMeta)]
 | 
			
		||||
        parents = [b for b in bases if isinstance(b, cls)]
 | 
			
		||||
        if not parents:
 | 
			
		||||
            # If this isn't a subclass of Model, don't do anything special.
 | 
			
		||||
            return super_new(cls, name, bases, attrs)
 | 
			
		||||
| 
						 | 
				
			
			@ -26,19 +32,20 @@ class ObjectTypeMeta(type):
 | 
			
		|||
        })
 | 
			
		||||
        attr_meta = attrs.pop('Meta', None)
 | 
			
		||||
        if not attr_meta:
 | 
			
		||||
            meta = getattr(new_class, 'Meta', None)
 | 
			
		||||
            meta = None
 | 
			
		||||
            # meta = getattr(new_class, 'Meta', None)
 | 
			
		||||
        else:
 | 
			
		||||
            meta = attr_meta
 | 
			
		||||
 | 
			
		||||
        base_meta = getattr(new_class, '_meta', None)
 | 
			
		||||
 | 
			
		||||
        schema = (base_meta and base_meta.schema)
 | 
			
		||||
 | 
			
		||||
        new_class.add_to_class('_meta', Options(meta, schema))
 | 
			
		||||
        if base_meta and base_meta.proxy:
 | 
			
		||||
            new_class._meta.interface = base_meta.interface
 | 
			
		||||
        new_class.add_to_class('_meta', new_class.options_cls(meta))
 | 
			
		||||
        
 | 
			
		||||
        new_class._meta.interface = new_class.is_interface(parents)
 | 
			
		||||
        # Add all attributes to the class.
 | 
			
		||||
        for obj_name, obj in attrs.items():
 | 
			
		||||
            new_class.add_to_class(obj_name, obj)
 | 
			
		||||
        new_class.add_extra_fields()
 | 
			
		||||
 | 
			
		||||
        new_fields = new_class._meta.local_fields
 | 
			
		||||
        field_names = {f.field_name for f in new_fields}
 | 
			
		||||
| 
						 | 
				
			
			@ -71,6 +78,9 @@ class ObjectTypeMeta(type):
 | 
			
		|||
        new_class._prepare()
 | 
			
		||||
        return new_class
 | 
			
		||||
 | 
			
		||||
    def add_extra_fields(cls):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def _prepare(cls):
 | 
			
		||||
        signals.class_prepared.send(cls)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -82,13 +92,13 @@ class ObjectTypeMeta(type):
 | 
			
		|||
            setattr(cls, name, value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ObjectType(six.with_metaclass(ObjectTypeMeta)):
 | 
			
		||||
class BaseObjectType(object):
 | 
			
		||||
    def __new__(cls, instance=None, *args, **kwargs):
 | 
			
		||||
        if cls._meta.interface:
 | 
			
		||||
            raise Exception("An interface cannot be initialized")
 | 
			
		||||
        if instance == 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):
 | 
			
		||||
        signals.pre_init.send(self.__class__, instance=instance)
 | 
			
		||||
| 
						 | 
				
			
			@ -117,45 +127,35 @@ class ObjectType(six.with_metaclass(ObjectTypeMeta)):
 | 
			
		|||
        return True
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def resolve_type(cls, instance, *_):
 | 
			
		||||
        return instance._meta.type
 | 
			
		||||
    def resolve_type(cls, schema, instance, *_):
 | 
			
		||||
        return instance.internal_type(schema)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_graphql_type(cls):
 | 
			
		||||
        fields = cls._meta.fields_map
 | 
			
		||||
    @memoize
 | 
			
		||||
    @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:
 | 
			
		||||
            return GraphQLInterfaceType(
 | 
			
		||||
                cls._meta.type_name,
 | 
			
		||||
                description=cls._meta.description,
 | 
			
		||||
                resolve_type=cls.resolve_type,
 | 
			
		||||
                fields=lambda: {name: field.field for name, field in fields.items()}
 | 
			
		||||
                resolve_type=lambda *args, **kwargs: cls.resolve_type(schema, *args, **kwargs),
 | 
			
		||||
                fields=fields
 | 
			
		||||
            )
 | 
			
		||||
        return GraphQLObjectType(
 | 
			
		||||
            cls._meta.type_name,
 | 
			
		||||
            description=cls._meta.description,
 | 
			
		||||
            interfaces=[i._meta.type for i in cls._meta.interfaces],
 | 
			
		||||
            fields=lambda: {name: field.field for name, field in fields.items()}
 | 
			
		||||
            interfaces=[i.internal_type(schema) for i in cls._meta.interfaces],
 | 
			
		||||
            fields=fields
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Interface(ObjectType):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        interface = True
 | 
			
		||||
        proxy = True
 | 
			
		||||
class ObjectType(six.with_metaclass(ObjectTypeMeta, BaseObjectType)):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@signals.init_schema.connect
 | 
			
		||||
def add_types_to_schema(schema):
 | 
			
		||||
    own_schema = schema
 | 
			
		||||
    class _Interface(Interface):
 | 
			
		||||
        class Meta:
 | 
			
		||||
            schema = own_schema
 | 
			
		||||
            proxy = True
 | 
			
		||||
 | 
			
		||||
    class _ObjectType(ObjectType):
 | 
			
		||||
        class Meta:
 | 
			
		||||
            schema = own_schema
 | 
			
		||||
            proxy = True
 | 
			
		||||
    
 | 
			
		||||
    setattr(own_schema, 'Interface', _Interface)
 | 
			
		||||
    setattr(own_schema, 'ObjectType', _ObjectType)
 | 
			
		||||
class Interface(six.with_metaclass(ObjectTypeMeta, BaseObjectType)):
 | 
			
		||||
    pass
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,20 +1,10 @@
 | 
			
		|||
from graphene.relay.nodes import (
 | 
			
		||||
	create_node_definitions
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
from graphene.relay.fields import (
 | 
			
		||||
	ConnectionField,
 | 
			
		||||
	NodeField
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
import graphene.relay.connections
 | 
			
		||||
 | 
			
		||||
from graphene.relay.relay import (
 | 
			
		||||
	Relay
 | 
			
		||||
from graphene.relay.types import (
 | 
			
		||||
	Node
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
from graphene.env import get_global_schema
 | 
			
		||||
 | 
			
		||||
schema = get_global_schema()
 | 
			
		||||
relay = schema.relay
 | 
			
		||||
 | 
			
		||||
Node, NodeField = relay.Node, relay.NodeField
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,35 +1,15 @@
 | 
			
		|||
import collections
 | 
			
		||||
 | 
			
		||||
from graphql_relay.node.node import (
 | 
			
		||||
    globalIdField
 | 
			
		||||
)
 | 
			
		||||
from graphql_relay.connection.connection import (
 | 
			
		||||
    connectionDefinitions
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
from graphene import signals
 | 
			
		||||
 | 
			
		||||
from graphene.core.fields import NativeField
 | 
			
		||||
from graphene.relay.utils import get_relay
 | 
			
		||||
from graphene.relay.relay import Relay
 | 
			
		||||
 | 
			
		||||
from graphene.relay.fields import NodeIDField
 | 
			
		||||
from graphene.relay.types import BaseNode, Node
 | 
			
		||||
 | 
			
		||||
@signals.class_prepared.connect
 | 
			
		||||
def object_type_created(object_type):
 | 
			
		||||
    relay = get_relay(object_type._meta.schema)
 | 
			
		||||
    if relay and issubclass(object_type, relay.Node):
 | 
			
		||||
    if issubclass(object_type, BaseNode) and BaseNode not in object_type.__bases__:
 | 
			
		||||
        type_name = object_type._meta.type_name
 | 
			
		||||
        # def getId(*args, **kwargs):
 | 
			
		||||
        #     print '**GET ID', args, kwargs
 | 
			
		||||
        #     return 2
 | 
			
		||||
        field = NativeField(globalIdField(type_name))
 | 
			
		||||
        field = NodeIDField()
 | 
			
		||||
        object_type.add_to_class('id', field)
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@signals.init_schema.connect
 | 
			
		||||
def schema_created(schema):
 | 
			
		||||
    setattr(schema, 'relay', Relay(schema))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,9 +6,13 @@ from graphql_relay.connection.arrayconnection import (
 | 
			
		|||
from graphql_relay.connection.connection import (
 | 
			
		||||
    connectionArgs
 | 
			
		||||
)
 | 
			
		||||
from graphene.core.fields import Field
 | 
			
		||||
from graphql_relay.node.node import (
 | 
			
		||||
    globalIdField
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
from graphene.core.fields import Field, LazyNativeField
 | 
			
		||||
from graphene.utils import cached_property
 | 
			
		||||
from graphene.relay.utils import get_relay
 | 
			
		||||
from graphene.utils import memoize
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ConnectionField(Field):
 | 
			
		||||
| 
						 | 
				
			
			@ -16,15 +20,30 @@ class ConnectionField(Field):
 | 
			
		|||
        super(ConnectionField, self).__init__(field_type, resolve=resolve, 
 | 
			
		||||
                                              args=connectionArgs, description=description)
 | 
			
		||||
 | 
			
		||||
    def wrap_resolved(self, value, instance, args, info):
 | 
			
		||||
        return value
 | 
			
		||||
 | 
			
		||||
    def resolve(self, instance, args, info):
 | 
			
		||||
        resolved = super(ConnectionField, self).resolve(instance, args, info)
 | 
			
		||||
        if resolved:
 | 
			
		||||
            resolved = self.wrap_resolved(resolved, instance, args, info)
 | 
			
		||||
            assert isinstance(resolved, collections.Iterable), 'Resolved value from the connection field have to be iterable'
 | 
			
		||||
            return connectionFromArray(resolved, args)
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def type(self):
 | 
			
		||||
        object_type = self.get_object_type()
 | 
			
		||||
        relay = get_relay(object_type._meta.schema)
 | 
			
		||||
        assert issubclass(object_type, relay.Node), 'Only nodes have connections.'
 | 
			
		||||
        return object_type.connection
 | 
			
		||||
    @memoize
 | 
			
		||||
    def internal_type(self, schema):
 | 
			
		||||
        from graphene.relay.types import BaseNode
 | 
			
		||||
        object_type = self.get_object_type(schema)
 | 
			
		||||
        assert issubclass(object_type, BaseNode), 'Only nodes have connections.'
 | 
			
		||||
        return object_type.get_connection(schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NodeField(LazyNativeField):
 | 
			
		||||
    def get_field(self, schema):
 | 
			
		||||
        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,42 +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 Field, NativeField
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def getSchemaNode(schema=None):
 | 
			
		||||
    def getNode(globalId, *args):
 | 
			
		||||
        _schema = schema or get_global_schema()
 | 
			
		||||
        resolvedGlobalId = fromGlobalId(globalId)
 | 
			
		||||
        _type, _id = resolvedGlobalId.type, resolvedGlobalId.id
 | 
			
		||||
        object_type = schema.get_type(_type) 
 | 
			
		||||
        return object_type.get_node(_id)
 | 
			
		||||
    return getNode
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def getNodeType(obj):
 | 
			
		||||
    return obj._meta.type
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_node_definitions(getNode=None, getNodeType=getNodeType, schema=None):
 | 
			
		||||
    getNode = getNode or getSchemaNode(schema)
 | 
			
		||||
    _nodeDefinitions = nodeDefinitions(getNode, getNodeType)
 | 
			
		||||
 | 
			
		||||
    _Interface = getattr(schema,'Interface', Interface)
 | 
			
		||||
 | 
			
		||||
    class Node(_Interface):
 | 
			
		||||
        @classmethod
 | 
			
		||||
        def get_graphql_type(cls):
 | 
			
		||||
            if cls is Node:
 | 
			
		||||
                # Return only nodeInterface when is the Node Inerface
 | 
			
		||||
                return _nodeDefinitions.nodeInterface
 | 
			
		||||
            return super(Node, cls).get_graphql_type()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class NodeField(NativeField):
 | 
			
		||||
        field = _nodeDefinitions.nodeField
 | 
			
		||||
 | 
			
		||||
    return Node, NodeField
 | 
			
		||||
| 
						 | 
				
			
			@ -1,14 +0,0 @@
 | 
			
		|||
from graphene.relay.nodes import (
 | 
			
		||||
	create_node_definitions
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
from graphene.relay.fields import (
 | 
			
		||||
	ConnectionField,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Relay(object):
 | 
			
		||||
    def __init__(self, schema):
 | 
			
		||||
        self.schema = schema
 | 
			
		||||
        self.Node, self.NodeField = create_node_definitions(schema=self.schema)
 | 
			
		||||
        self.ConnectionField = ConnectionField
 | 
			
		||||
							
								
								
									
										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 +0,0 @@
 | 
			
		|||
 | 
			
		||||
def get_relay(schema):
 | 
			
		||||
    return getattr(schema, 'relay', None)
 | 
			
		||||
| 
						 | 
				
			
			@ -1,3 +1,5 @@
 | 
			
		|||
from functools import wraps
 | 
			
		||||
 | 
			
		||||
class cached_property(object):
 | 
			
		||||
    """
 | 
			
		||||
    A property that is only computed once per instance and then replaces itself
 | 
			
		||||
| 
						 | 
				
			
			@ -14,3 +16,17 @@ class cached_property(object):
 | 
			
		|||
            return self
 | 
			
		||||
        value = obj.__dict__[self.func.__name__] = self.func(obj)
 | 
			
		||||
        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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										1
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								setup.py
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -56,6 +56,7 @@ setup(
 | 
			
		|||
    extras_require={
 | 
			
		||||
        'django': [
 | 
			
		||||
            'Django>=1.8.0,<1.9',
 | 
			
		||||
            'pytest-django',
 | 
			
		||||
            'singledispatch>=3.4.0.3',
 | 
			
		||||
        ],
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										0
									
								
								tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								tests/contrib_django/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/contrib_django/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										17
									
								
								tests/contrib_django/data.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/contrib_django/data.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
from datetime import date
 | 
			
		||||
 | 
			
		||||
from .models import Reporter, Article
 | 
			
		||||
 | 
			
		||||
r = Reporter(first_name='John', last_name='Smith', email='john@example.com')
 | 
			
		||||
r.save()
 | 
			
		||||
 | 
			
		||||
r2 = Reporter(first_name='Paul', last_name='Jones', email='paul@example.com')
 | 
			
		||||
r2.save()
 | 
			
		||||
 | 
			
		||||
a = Article(id=None, headline="This is a test", pub_date=date(2005, 7, 27), reporter=r)
 | 
			
		||||
a.save()
 | 
			
		||||
 | 
			
		||||
new_article = r.articles.create(headline="John's second story", pub_date=date(2005, 7, 29))
 | 
			
		||||
 | 
			
		||||
new_article2 = Article(headline="Paul's story", pub_date=date(2006, 1, 17))
 | 
			
		||||
r.articles.add(new_article2)
 | 
			
		||||
							
								
								
									
										25
									
								
								tests/contrib_django/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								tests/contrib_django/models.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,25 @@
 | 
			
		|||
from __future__ import absolute_import
 | 
			
		||||
from django.db import models
 | 
			
		||||
 | 
			
		||||
class Reporter(models.Model):
 | 
			
		||||
    first_name = models.CharField(max_length=30)
 | 
			
		||||
    last_name = models.CharField(max_length=30)
 | 
			
		||||
    email = models.EmailField()
 | 
			
		||||
 | 
			
		||||
    def __str__(self):              # __unicode__ on Python 2
 | 
			
		||||
        return "%s %s" % (self.first_name, self.last_name)
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        app_label = 'contrib_django'
 | 
			
		||||
 | 
			
		||||
class Article(models.Model):
 | 
			
		||||
    headline = models.CharField(max_length=100)
 | 
			
		||||
    pub_date = models.DateField()
 | 
			
		||||
    reporter = models.ForeignKey(Reporter, related_name='articles')
 | 
			
		||||
 | 
			
		||||
    def __str__(self):              # __unicode__ on Python 2
 | 
			
		||||
        return self.headline
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        ordering = ('headline',)
 | 
			
		||||
        app_label = 'contrib_django'
 | 
			
		||||
							
								
								
									
										170
									
								
								tests/contrib_django/test_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								tests/contrib_django/test_schema.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,170 @@
 | 
			
		|||
from py.test import raises
 | 
			
		||||
from collections import namedtuple
 | 
			
		||||
from pytest import raises
 | 
			
		||||
import graphene
 | 
			
		||||
from graphene import relay
 | 
			
		||||
from graphene.contrib.django import (
 | 
			
		||||
    DjangoObjectType,
 | 
			
		||||
    DjangoNode
 | 
			
		||||
)
 | 
			
		||||
from .models import Reporter, Article
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_should_raise_if_no_model():
 | 
			
		||||
    with raises(Exception) as excinfo:
 | 
			
		||||
        class Character1(DjangoObjectType):
 | 
			
		||||
            pass
 | 
			
		||||
    assert 'model in the Meta' in str(excinfo.value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_should_raise_if_model_is_invalid():
 | 
			
		||||
    with raises(Exception) as excinfo:
 | 
			
		||||
        class Character2(DjangoObjectType):
 | 
			
		||||
            class Meta:
 | 
			
		||||
                model = 1
 | 
			
		||||
    assert 'not a Django model' in str(excinfo.value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_should_raise_if_model_is_invalid():
 | 
			
		||||
    with raises(Exception) as excinfo:
 | 
			
		||||
        class ReporterTypeError(DjangoObjectType):
 | 
			
		||||
            class Meta:
 | 
			
		||||
                model = Reporter
 | 
			
		||||
                only_fields = ('articles', )
 | 
			
		||||
 | 
			
		||||
        schema = graphene.Schema(query=ReporterTypeError)
 | 
			
		||||
        query = '''
 | 
			
		||||
            query ReporterQuery {
 | 
			
		||||
              articles
 | 
			
		||||
            }
 | 
			
		||||
        '''
 | 
			
		||||
        result = schema.execute(query)
 | 
			
		||||
        assert not result.errors
 | 
			
		||||
 | 
			
		||||
    assert 'articles (Article) model not mapped in current schema' in str(excinfo.value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_should_map_fields_correctly():
 | 
			
		||||
    class ReporterType2(DjangoObjectType):
 | 
			
		||||
        class Meta:
 | 
			
		||||
            model = Reporter
 | 
			
		||||
    assert ReporterType2._meta.fields_map.keys() == ['articles', 'first_name', 'last_name', 'id', 'email']
 | 
			
		||||
 | 
			
		||||
def test_should_map_fields():
 | 
			
		||||
    class ReporterType(DjangoObjectType):
 | 
			
		||||
        class Meta:
 | 
			
		||||
            model = Reporter
 | 
			
		||||
 | 
			
		||||
    class Query2(graphene.ObjectType):
 | 
			
		||||
        reporter = graphene.Field(ReporterType)
 | 
			
		||||
 | 
			
		||||
        def resolve_reporter(self, *args, **kwargs):
 | 
			
		||||
            return ReporterType(Reporter(first_name='ABA', last_name='X'))
 | 
			
		||||
 | 
			
		||||
    query = '''
 | 
			
		||||
        query ReporterQuery {
 | 
			
		||||
          reporter {
 | 
			
		||||
            first_name,
 | 
			
		||||
            last_name,
 | 
			
		||||
            email
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
    '''
 | 
			
		||||
    expected = {
 | 
			
		||||
        'reporter': {
 | 
			
		||||
            'first_name': 'ABA',
 | 
			
		||||
            'last_name': 'X',
 | 
			
		||||
            'email': ''
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    Schema = graphene.Schema(query=Query2)
 | 
			
		||||
    result = Schema.execute(query)
 | 
			
		||||
    assert not result.errors
 | 
			
		||||
    assert result.data == expected
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_should_map_only_few_fields():
 | 
			
		||||
    class Reporter2(DjangoObjectType):
 | 
			
		||||
        class Meta:
 | 
			
		||||
            model = Reporter
 | 
			
		||||
            only_fields = ('id', 'email')
 | 
			
		||||
    assert Reporter2._meta.fields_map.keys() == ['id', 'email']
 | 
			
		||||
 | 
			
		||||
def test_should_node():
 | 
			
		||||
    class ReporterNodeType(DjangoNode):
 | 
			
		||||
        class Meta:
 | 
			
		||||
            model = Reporter
 | 
			
		||||
 | 
			
		||||
        @classmethod
 | 
			
		||||
        def get_node(cls, id):
 | 
			
		||||
            return ReporterNodeType(Reporter(id=2, first_name='Cookie Monster'))
 | 
			
		||||
 | 
			
		||||
        def resolve_articles(self, *args, **kwargs):
 | 
			
		||||
            return [ArticleNodeType(Article(headline='Hi!'))]
 | 
			
		||||
 | 
			
		||||
    class ArticleNodeType(DjangoNode):
 | 
			
		||||
        class Meta:
 | 
			
		||||
            model = Article
 | 
			
		||||
 | 
			
		||||
        @classmethod
 | 
			
		||||
        def get_node(cls, id):
 | 
			
		||||
            return ArticleNodeType(Article(id=1, headline='Article node'))
 | 
			
		||||
 | 
			
		||||
    class Query1(graphene.ObjectType):
 | 
			
		||||
        node = relay.NodeField()
 | 
			
		||||
        reporter = graphene.Field(ReporterNodeType)
 | 
			
		||||
        article = graphene.Field(ArticleNodeType)
 | 
			
		||||
 | 
			
		||||
        def resolve_reporter(self, *args, **kwargs):
 | 
			
		||||
            return ReporterNodeType(Reporter(id=1, first_name='ABA', last_name='X'))
 | 
			
		||||
 | 
			
		||||
    query = '''
 | 
			
		||||
        query ReporterQuery {
 | 
			
		||||
          reporter {
 | 
			
		||||
            id,
 | 
			
		||||
            first_name,
 | 
			
		||||
            articles {
 | 
			
		||||
              edges {
 | 
			
		||||
                node {
 | 
			
		||||
                  headline
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            last_name,
 | 
			
		||||
            email
 | 
			
		||||
          }
 | 
			
		||||
          my_article: node(id:"QXJ0aWNsZU5vZGVUeXBlOjE=") {
 | 
			
		||||
            id
 | 
			
		||||
            ... on ReporterNodeType {
 | 
			
		||||
                first_name
 | 
			
		||||
            }
 | 
			
		||||
            ... on ArticleNodeType {
 | 
			
		||||
                headline
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
    '''
 | 
			
		||||
    expected = {
 | 
			
		||||
        'reporter': {
 | 
			
		||||
            'id': 'UmVwb3J0ZXJOb2RlVHlwZTox',
 | 
			
		||||
            'first_name': 'ABA',
 | 
			
		||||
            'last_name': 'X',
 | 
			
		||||
            'email': '',
 | 
			
		||||
            'articles': {
 | 
			
		||||
              'edges': [{
 | 
			
		||||
                'node': {
 | 
			
		||||
                  'headline': 'Hi!'
 | 
			
		||||
                }
 | 
			
		||||
              }]
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
        'my_article': {
 | 
			
		||||
            'id': 'QXJ0aWNsZU5vZGVUeXBlOjE=',
 | 
			
		||||
            'headline': 'Article node'
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    Schema = graphene.Schema(query=Query1)
 | 
			
		||||
    result = Schema.execute(query)
 | 
			
		||||
    assert not result.errors
 | 
			
		||||
    assert result.data == expected
 | 
			
		||||
							
								
								
									
										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')
 | 
			
		||||
 | 
			
		||||
class Schema(object):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
schema = Schema()
 | 
			
		||||
 | 
			
		||||
def test_field_no_contributed_raises_error():
 | 
			
		||||
    f = Field(GraphQLString)
 | 
			
		||||
    with raises(Exception) as excinfo:
 | 
			
		||||
        f.field
 | 
			
		||||
        f.internal_field(schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_field_type():
 | 
			
		||||
    f = Field(GraphQLString)
 | 
			
		||||
    f.contribute_to_class(ot, 'field_name')
 | 
			
		||||
    assert isinstance(f.field, GraphQLField)
 | 
			
		||||
    assert f.type == GraphQLString
 | 
			
		||||
    assert isinstance(f.internal_field(schema), GraphQLField)
 | 
			
		||||
    assert f.internal_type(schema) == GraphQLString
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_stringfield_type():
 | 
			
		||||
    f = StringField()
 | 
			
		||||
    f.contribute_to_class(ot, 'field_name')
 | 
			
		||||
    assert f.type == GraphQLString
 | 
			
		||||
    assert f.internal_type(schema) == GraphQLString
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_stringfield_type_null():
 | 
			
		||||
    f = StringField(null=False)
 | 
			
		||||
    f.contribute_to_class(ot, 'field_name')
 | 
			
		||||
    assert isinstance(f.field, GraphQLField)
 | 
			
		||||
    assert isinstance(f.type, GraphQLNonNull)
 | 
			
		||||
    assert isinstance(f.internal_field(schema), GraphQLField)
 | 
			
		||||
    assert isinstance(f.internal_type(schema), GraphQLNonNull)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_field_resolve():
 | 
			
		||||
    f = StringField(null=False)
 | 
			
		||||
    f = StringField(null=False, resolve=lambda *args:'RESOLVED')
 | 
			
		||||
    f.contribute_to_class(ot, 'field_name')
 | 
			
		||||
    field_type = f.field
 | 
			
		||||
    field_type.resolver(ot,2,3)
 | 
			
		||||
    field_type = f.internal_field(schema)
 | 
			
		||||
    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
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Character(Interface):
 | 
			
		||||
    '''Character description'''
 | 
			
		||||
    name = StringField()
 | 
			
		||||
    class Meta:
 | 
			
		||||
        type_name = 'core.Character'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Human(Character):
 | 
			
		||||
    '''Human description'''
 | 
			
		||||
    friends = StringField()
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        type_name = 'core.Human'
 | 
			
		||||
 | 
			
		||||
schema = object()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_interface():
 | 
			
		||||
    object_type = Character._meta.type
 | 
			
		||||
    object_type = Character.internal_type(schema)
 | 
			
		||||
    assert Character._meta.interface == True
 | 
			
		||||
    assert Character._meta.type_name == 'core.Character'
 | 
			
		||||
    assert isinstance(object_type, GraphQLInterfaceType)
 | 
			
		||||
    assert Character._meta.type_name == 'core.Character'
 | 
			
		||||
    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():
 | 
			
		||||
    object_type = Human._meta.type
 | 
			
		||||
    object_type = Human.internal_type(schema)
 | 
			
		||||
    assert Human._meta.interface == False
 | 
			
		||||
    assert Human._meta.type_name == 'core.Human'
 | 
			
		||||
    assert isinstance(object_type, GraphQLObjectType)
 | 
			
		||||
    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_interfaces() == [Character._meta.type]
 | 
			
		||||
    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.internal_type(schema)]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								tests/django_settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tests/django_settings.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
SECRET_KEY = 1
 | 
			
		||||
 | 
			
		||||
INSTALLED_APPS = [
 | 
			
		||||
    'graphene.contrib.django',
 | 
			
		||||
    'tests.starwars_django',
 | 
			
		||||
    'tests.contrib_django',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
DATABASES={
 | 
			
		||||
    'default': {
 | 
			
		||||
        'ENGINE': 'django.db.backends.sqlite3',
 | 
			
		||||
        'NAME': 'tests/django.sqlite',
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4,7 +4,6 @@ import graphene
 | 
			
		|||
from graphene import relay
 | 
			
		||||
 | 
			
		||||
schema = graphene.Schema()
 | 
			
		||||
relay = schema.relay
 | 
			
		||||
 | 
			
		||||
class OtherNode(relay.Node):
 | 
			
		||||
    name = graphene.StringField()
 | 
			
		||||
| 
						 | 
				
			
			@ -22,8 +21,12 @@ def test_field_no_contributed_raises_error():
 | 
			
		|||
    assert 'get_node' in str(excinfo.value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_node_should_have_connection():
 | 
			
		||||
    assert OtherNode.connection
 | 
			
		||||
def test_node_should_have_same_connection_always():
 | 
			
		||||
    s = object()
 | 
			
		||||
    connection1 = OtherNode.get_connection(s)
 | 
			
		||||
    connection2 = OtherNode.get_connection(s)
 | 
			
		||||
 | 
			
		||||
    assert connection1 == connection2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_node_should_have_id_field():
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										0
									
								
								tests/starwars_django/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/starwars_django/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										101
									
								
								tests/starwars_django/data.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								tests/starwars_django/data.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,101 @@
 | 
			
		|||
from collections import namedtuple
 | 
			
		||||
 | 
			
		||||
from .models import Ship, Faction
 | 
			
		||||
 | 
			
		||||
def initialize():
 | 
			
		||||
    rebels = Faction(
 | 
			
		||||
        id='1',
 | 
			
		||||
        name='Alliance to Restore the Republic',
 | 
			
		||||
    )
 | 
			
		||||
    rebels.save()
 | 
			
		||||
 | 
			
		||||
    empire = Faction(
 | 
			
		||||
        id='2',
 | 
			
		||||
        name='Galactic Empire',
 | 
			
		||||
    )
 | 
			
		||||
    empire.save()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    xwing = Ship(
 | 
			
		||||
        id='1',
 | 
			
		||||
        name='X-Wing',
 | 
			
		||||
        faction=rebels,
 | 
			
		||||
    )
 | 
			
		||||
    xwing.save()
 | 
			
		||||
 | 
			
		||||
    ywing = Ship(
 | 
			
		||||
        id='2',
 | 
			
		||||
        name='Y-Wing',
 | 
			
		||||
        faction=rebels,
 | 
			
		||||
    )
 | 
			
		||||
    ywing.save()
 | 
			
		||||
 | 
			
		||||
    awing = Ship(
 | 
			
		||||
        id='3',
 | 
			
		||||
        name='A-Wing',
 | 
			
		||||
        faction=rebels,
 | 
			
		||||
    )
 | 
			
		||||
    awing.save()
 | 
			
		||||
 | 
			
		||||
    # Yeah, technically it's Corellian. But it flew in the service of the rebels,
 | 
			
		||||
    # so for the purposes of this demo it's a rebel ship.
 | 
			
		||||
    falcon = Ship(
 | 
			
		||||
        id='4',
 | 
			
		||||
        name='Millenium Falcon',
 | 
			
		||||
        faction=rebels,
 | 
			
		||||
    )
 | 
			
		||||
    falcon.save()
 | 
			
		||||
 | 
			
		||||
    homeOne = Ship(
 | 
			
		||||
        id='5',
 | 
			
		||||
        name='Home One',
 | 
			
		||||
        faction=rebels,
 | 
			
		||||
    )
 | 
			
		||||
    homeOne.save()
 | 
			
		||||
 | 
			
		||||
    tieFighter = Ship(
 | 
			
		||||
        id='6',
 | 
			
		||||
        name='TIE Fighter',
 | 
			
		||||
        faction=empire,
 | 
			
		||||
    )
 | 
			
		||||
    tieFighter.save()
 | 
			
		||||
 | 
			
		||||
    tieInterceptor = Ship(
 | 
			
		||||
        id='7',
 | 
			
		||||
        name='TIE Interceptor',
 | 
			
		||||
        faction=empire,
 | 
			
		||||
    )
 | 
			
		||||
    tieInterceptor.save()
 | 
			
		||||
 | 
			
		||||
    executor = Ship(
 | 
			
		||||
        id='8',
 | 
			
		||||
        name='Executor',
 | 
			
		||||
        faction=empire,
 | 
			
		||||
    )
 | 
			
		||||
    executor.save()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def createShip(shipName, factionId):
 | 
			
		||||
    nextShip = len(data['Ship'].keys())+1
 | 
			
		||||
    newShip = Ship(
 | 
			
		||||
        id=str(nextShip),
 | 
			
		||||
        name=shipName
 | 
			
		||||
    )
 | 
			
		||||
    newShip.save()
 | 
			
		||||
    return newShip
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def getShip(_id):
 | 
			
		||||
    return Ship.objects.get(id=_id)
 | 
			
		||||
 | 
			
		||||
def getShips():
 | 
			
		||||
    return Ship.objects.all()
 | 
			
		||||
 | 
			
		||||
def getFaction(_id):
 | 
			
		||||
    return Faction.objects.get(id=_id)
 | 
			
		||||
 | 
			
		||||
def getRebels():
 | 
			
		||||
    return getFaction(1)
 | 
			
		||||
 | 
			
		||||
def getEmpire():
 | 
			
		||||
    return getFaction(2)
 | 
			
		||||
							
								
								
									
										17
									
								
								tests/starwars_django/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/starwars_django/models.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
from __future__ import absolute_import
 | 
			
		||||
from django.db import models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Faction(models.Model):
 | 
			
		||||
    name = models.CharField(max_length=50)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.name
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Ship(models.Model):
 | 
			
		||||
    name = models.CharField(max_length=50)
 | 
			
		||||
    faction = models.ForeignKey(Faction, related_name='ships')
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.name
 | 
			
		||||
							
								
								
									
										54
									
								
								tests/starwars_django/schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								tests/starwars_django/schema.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,54 @@
 | 
			
		|||
import graphene
 | 
			
		||||
from graphene import resolve_only_args, relay
 | 
			
		||||
from graphene.contrib.django import (
 | 
			
		||||
    DjangoObjectType,
 | 
			
		||||
    DjangoNode
 | 
			
		||||
)
 | 
			
		||||
from .models import Ship as ShipModel, Faction as FactionModel
 | 
			
		||||
from .data import (
 | 
			
		||||
    getFaction,
 | 
			
		||||
    getShip,
 | 
			
		||||
    getShips,
 | 
			
		||||
    getRebels,
 | 
			
		||||
    getEmpire,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
schema = graphene.Schema(name='Starwars Django Relay Schema')
 | 
			
		||||
 | 
			
		||||
class Ship(DjangoNode):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = ShipModel    
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_node(cls, id):
 | 
			
		||||
        return Ship(getShip(id))
 | 
			
		||||
 | 
			
		||||
class Faction(DjangoNode):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = FactionModel    
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_node(cls, id):
 | 
			
		||||
        return Faction(getFaction(id))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Query(graphene.ObjectType):
 | 
			
		||||
    rebels = graphene.Field(Faction)
 | 
			
		||||
    empire = graphene.Field(Faction)
 | 
			
		||||
    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
 | 
			
		||||
    def resolve_rebels(self):
 | 
			
		||||
        return Faction(getRebels())
 | 
			
		||||
 | 
			
		||||
    @resolve_only_args
 | 
			
		||||
    def resolve_empire(self):
 | 
			
		||||
        return Faction(getEmpire())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
schema.query = Query
 | 
			
		||||
							
								
								
									
										42
									
								
								tests/starwars_django/test_connections.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								tests/starwars_django/test_connections.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
import pytest
 | 
			
		||||
from graphql.core import graphql
 | 
			
		||||
 | 
			
		||||
from .models import *
 | 
			
		||||
from .schema import schema
 | 
			
		||||
from .data import initialize
 | 
			
		||||
 | 
			
		||||
pytestmark = pytest.mark.django_db
 | 
			
		||||
 | 
			
		||||
def test_correct_fetch_first_ship_rebels():
 | 
			
		||||
    initialize()
 | 
			
		||||
    query = '''
 | 
			
		||||
    query RebelsShipsQuery {
 | 
			
		||||
      rebels {
 | 
			
		||||
        name,
 | 
			
		||||
        ships(first: 1) {
 | 
			
		||||
          edges {
 | 
			
		||||
            node {
 | 
			
		||||
              name
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    '''
 | 
			
		||||
    expected = {
 | 
			
		||||
      'rebels': {
 | 
			
		||||
        'name': 'Alliance to Restore the Republic',
 | 
			
		||||
        'ships': {
 | 
			
		||||
          'edges': [
 | 
			
		||||
            {
 | 
			
		||||
              'node': {
 | 
			
		||||
                'name': 'X-Wing'
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    result = schema.execute(query)
 | 
			
		||||
    assert not result.errors
 | 
			
		||||
    assert result.data == expected
 | 
			
		||||
							
								
								
									
										114
									
								
								tests/starwars_django/test_objectidentification.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								tests/starwars_django/test_objectidentification.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,114 @@
 | 
			
		|||
import pytest
 | 
			
		||||
from pytest import raises
 | 
			
		||||
from graphql.core import graphql
 | 
			
		||||
from .data import initialize
 | 
			
		||||
 | 
			
		||||
from .schema import schema
 | 
			
		||||
 | 
			
		||||
pytestmark = pytest.mark.django_db
 | 
			
		||||
 | 
			
		||||
def test_correctly_fetches_id_name_rebels():
 | 
			
		||||
    initialize()
 | 
			
		||||
    query = '''
 | 
			
		||||
      query RebelsQuery {
 | 
			
		||||
        rebels {
 | 
			
		||||
          id
 | 
			
		||||
          name
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    '''
 | 
			
		||||
    expected = {
 | 
			
		||||
      'rebels': {
 | 
			
		||||
        'id': 'RmFjdGlvbjox',
 | 
			
		||||
        'name': 'Alliance to Restore the Republic'
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    result = schema.execute(query)
 | 
			
		||||
    assert not result.errors
 | 
			
		||||
    assert result.data == expected
 | 
			
		||||
 | 
			
		||||
def test_correctly_refetches_rebels():
 | 
			
		||||
    initialize()
 | 
			
		||||
    query = '''
 | 
			
		||||
      query RebelsRefetchQuery {
 | 
			
		||||
        node(id: "RmFjdGlvbjox") {
 | 
			
		||||
          id
 | 
			
		||||
          ... on Faction {
 | 
			
		||||
            name
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    '''
 | 
			
		||||
    expected = {
 | 
			
		||||
      'node': {
 | 
			
		||||
        'id': 'RmFjdGlvbjox',
 | 
			
		||||
        'name': 'Alliance to Restore the Republic'
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    result = schema.execute(query)
 | 
			
		||||
    assert not result.errors
 | 
			
		||||
    assert result.data == expected
 | 
			
		||||
 | 
			
		||||
def test_correctly_fetches_id_name_empire():
 | 
			
		||||
    initialize()
 | 
			
		||||
    query = '''
 | 
			
		||||
      query EmpireQuery {
 | 
			
		||||
        empire {
 | 
			
		||||
          id
 | 
			
		||||
          name
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    '''
 | 
			
		||||
    expected = {
 | 
			
		||||
      'empire': {
 | 
			
		||||
        'id': 'RmFjdGlvbjoy',
 | 
			
		||||
        'name': 'Galactic Empire'
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    result = schema.execute(query)
 | 
			
		||||
    assert not result.errors
 | 
			
		||||
    assert result.data == expected
 | 
			
		||||
 | 
			
		||||
def test_correctly_refetches_empire():
 | 
			
		||||
    initialize()
 | 
			
		||||
    query = '''
 | 
			
		||||
      query EmpireRefetchQuery {
 | 
			
		||||
        node(id: "RmFjdGlvbjoy") {
 | 
			
		||||
          id
 | 
			
		||||
          ... on Faction {
 | 
			
		||||
            name
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    '''
 | 
			
		||||
    expected = {
 | 
			
		||||
      'node': {
 | 
			
		||||
        'id': 'RmFjdGlvbjoy',
 | 
			
		||||
        'name': 'Galactic Empire'
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    result = schema.execute(query)
 | 
			
		||||
    assert not result.errors
 | 
			
		||||
    assert result.data == expected
 | 
			
		||||
 | 
			
		||||
def test_correctly_refetches_xwing():
 | 
			
		||||
    initialize()
 | 
			
		||||
    query = '''
 | 
			
		||||
      query XWingRefetchQuery {
 | 
			
		||||
        node(id: "U2hpcDox") {
 | 
			
		||||
          id
 | 
			
		||||
          ... on Ship {
 | 
			
		||||
            name
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    '''
 | 
			
		||||
    expected = {
 | 
			
		||||
      'node': {
 | 
			
		||||
        'id': 'U2hpcDox',
 | 
			
		||||
        'name': 'X-Wing'
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    result = schema.execute(query)
 | 
			
		||||
    assert not result.errors
 | 
			
		||||
    assert result.data == expected
 | 
			
		||||
| 
						 | 
				
			
			@ -8,6 +8,8 @@ from .data import (
 | 
			
		|||
    getEmpire,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
schema = graphene.Schema(name='Starwars Relay Schema')
 | 
			
		||||
 | 
			
		||||
class Ship(relay.Node):
 | 
			
		||||
    '''A ship in the Star Wars saga'''
 | 
			
		||||
    name = graphene.StringField(description='The name of the ship.')
 | 
			
		||||
| 
						 | 
				
			
			@ -45,4 +47,4 @@ class Query(graphene.ObjectType):
 | 
			
		|||
        return Faction(getEmpire())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
schema = graphene.Schema(query=Query, name='Starwars Relay Schema')
 | 
			
		||||
schema.query = Query
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user