diff --git a/graphene/contrib/django/converter.py b/graphene/contrib/django/converter.py index b1ddc791..d5799a76 100644 --- a/graphene/contrib/django/converter.py +++ b/graphene/contrib/django/converter.py @@ -5,6 +5,7 @@ from graphene.contrib.django.fields import (ConnectionOrListField, DjangoModelField) from graphene.core.fields import (BooleanField, FloatField, IDField, IntField, StringField) +from graphene.core.types.scalars import Boolean, Float, ID, Int, String try: UUIDField = models.UUIDField @@ -28,12 +29,12 @@ def convert_django_field(field): @convert_django_field.register(models.URLField) @convert_django_field.register(UUIDField) def convert_field_to_string(field): - return StringField(description=field.help_text) + return String(description=field.help_text) @convert_django_field.register(models.AutoField) def convert_field_to_id(field): - return IDField(description=field.help_text) + return ID(description=field.help_text) @convert_django_field.register(models.PositiveIntegerField) @@ -42,23 +43,23 @@ def convert_field_to_id(field): @convert_django_field.register(models.BigIntegerField) @convert_django_field.register(models.IntegerField) def convert_field_to_int(field): - return IntField(description=field.help_text) + return Int(description=field.help_text) @convert_django_field.register(models.BooleanField) def convert_field_to_boolean(field): - return BooleanField(description=field.help_text, required=True) + return Boolean(description=field.help_text, required=True) @convert_django_field.register(models.NullBooleanField) def convert_field_to_nullboolean(field): - return BooleanField(description=field.help_text) + return Boolean(description=field.help_text) @convert_django_field.register(models.DecimalField) @convert_django_field.register(models.FloatField) def convert_field_to_float(field): - return FloatField(description=field.help_text) + return Float(description=field.help_text) @convert_django_field.register(models.ManyToManyField) diff --git a/graphene/contrib/django/fields.py b/graphene/contrib/django/fields.py index 3abbfa94..b95a6d79 100644 --- a/graphene/contrib/django/fields.py +++ b/graphene/contrib/django/fields.py @@ -1,6 +1,7 @@ from graphene import relay from graphene.contrib.django.utils import get_type_for_model, lazy_map -from graphene.core.fields import Field, LazyField, ListField +from graphene.core.fields import Field, ListField +from graphene.core.types.base import FieldType from graphene.relay.utils import is_node @@ -8,60 +9,49 @@ class DjangoConnectionField(relay.ConnectionField): def wrap_resolved(self, value, instance, args, info): schema = info.schema.graphene_schema - return lazy_map(value, self.get_object_type(schema)) + return lazy_map(value, self.type.get_object_type(schema)) class LazyListField(ListField): - def resolve(self, instance, args, info): + def resolver(self, instance, args, info): schema = info.schema.graphene_schema - resolved = super(LazyListField, self).resolve(instance, args, info) + resolved = super(LazyListField, self).resolver(instance, args, info) return lazy_map(resolved, self.get_object_type(schema)) -class ConnectionOrListField(LazyField): +class ConnectionOrListField(Field): - def get_field(self, schema): - model_field = self.field_type + def internal_type(self, schema): + model_field = self.type field_object_type = model_field.get_object_type(schema) if is_node(field_object_type): field = DjangoConnectionField(model_field) else: field = LazyListField(model_field) field.contribute_to_class(self.object_type, self.name) - return field + return field.internal_type(schema) -class DjangoModelField(Field): +class DjangoModelField(FieldType): def __init__(self, model, *args, **kwargs): - super(DjangoModelField, self).__init__(None, *args, **kwargs) self.model = model - - def resolve(self, instance, args, info): - resolved = super(DjangoModelField, self).resolve(instance, args, info) - schema = info.schema.graphene_schema - _type = self.get_object_type(schema) - assert _type, ("Field %s cannot be retrieved as the " - "ObjectType is not registered by the schema" % ( - self.attname - )) - return _type(resolved) + super(DjangoModelField, self).__init__(*args, **kwargs) def internal_type(self, schema): _type = self.get_object_type(schema) - if not _type and self.object_type._meta.only_fields: + if not _type and self.parent._meta.only_fields: raise Exception( "Model %r is not accessible by the schema. " "You can either register the type manually " "using @schema.register. " - "Or disable the field %s in %s" % ( + "Or disable the field in %s" % ( self.model, - self.attname, - self.object_type + self.parent, ) ) - return schema.T(_type) or Field.SKIP + return schema.T(_type) def get_object_type(self, schema): return get_type_for_model(schema, self.model) diff --git a/graphene/core/schema.py b/graphene/core/schema.py index 171b0171..49147e48 100644 --- a/graphene/core/schema.py +++ b/graphene/core/schema.py @@ -40,9 +40,9 @@ class Schema(object): if object_type not in self._types: internal_type = object_type.internal_type(self) self._types[object_type] = internal_type - name = getattr(internal_type, 'name', None) - if name: - self._types_names[name] = object_type + is_objecttype = inspect.isclass(object_type) and issubclass(object_type, BaseObjectType) + if is_objecttype: + self.register(object_type) return self._types[object_type] else: return object_type @@ -65,6 +65,10 @@ class Schema(object): return GraphQLSchema(self, query=self.T(self.query), mutation=self.T(self.mutation)) def register(self, object_type): + type_name = object_type._meta.type_name + registered_object_type = self._types_names.get(type_name, None) + if registered_object_type: + assert registered_object_type == object_type, 'Type {} already registered with other object type'.format(type_name) self._types_names[object_type._meta.type_name] = object_type return object_type diff --git a/graphene/core/types/field.py b/graphene/core/types/field.py index eae3ae69..61098485 100644 --- a/graphene/core/types/field.py +++ b/graphene/core/types/field.py @@ -31,14 +31,12 @@ class Field(OrderedType): if isinstance(type, six.string_types): type = LazyType(type) self.required = required - if self.required: - type = NonNull(type) self.type = type self.description = description args = OrderedDict(args or {}, **kwargs) self.arguments = ArgumentsGroup(*args_list, **args) self.object_type = None - self.resolver = resolver + self.resolver_fn = resolver self.default = default def contribute_to_class(self, cls, attname): @@ -54,11 +52,11 @@ class Field(OrderedType): @property def resolver(self): - return self._resolver or self.get_resolver_fn() + return self.resolver_fn or self.get_resolver_fn() @resolver.setter def resolver(self, value): - self._resolver = value + self.resolver_fn = value def get_resolver_fn(self): resolve_fn_name = 'resolve_%s' % self.attname @@ -70,6 +68,8 @@ class Field(OrderedType): return default_getter def get_type(self, schema): + if self.required: + return NonNull(self.type) return self.type def internal_type(self, schema): diff --git a/graphene/relay/fields.py b/graphene/relay/fields.py index 855dfbfd..047ac1d5 100644 --- a/graphene/relay/fields.py +++ b/graphene/relay/fields.py @@ -1,7 +1,7 @@ from collections import Iterable from graphene.core.fields import Field, IDField -from graphene.core.types.scalars import String, ID +from graphene.core.types.scalars import String, ID, Int from graphql.core.type import GraphQLArgument, GraphQLID, GraphQLNonNull from graphql_relay.connection.arrayconnection import connection_from_list from graphql_relay.node.node import from_global_id @@ -14,8 +14,8 @@ class ConnectionField(Field): super(ConnectionField, self).__init__(field_type, resolver=resolver, before=String(), after=String(), - first=String(), - last=String(), + first=Int(), + last=Int(), description=description, **kwargs) self.connection_type = connection_type self.edge_type = edge_type @@ -24,17 +24,18 @@ class ConnectionField(Field): return value - def resolve(self, instance, args, info): + def resolver(self, instance, args, info): from graphene.relay.types import PageInfo schema = info.schema.graphene_schema - resolved = super(ConnectionField, self).resolve(instance, args, info) + resolved = super(ConnectionField, self).resolver(instance, args, info) if resolved: resolved = self.wrap_resolved(resolved, instance, args, info) assert isinstance( resolved, Iterable), 'Resolved value from the connection field have to be iterable' - node = self.get_object_type(schema) + type = schema.T(self.type) + node = schema.objecttype(type) connection_type = self.get_connection_type(node) edge_type = self.get_edge_type(node) @@ -81,14 +82,16 @@ class NodeField(Field): return object_type.get_node(_id) - def resolve(self, instance, args, info): + def resolver(self, instance, args, info): global_id = args.get('id') return self.id_fetcher(global_id, info) -class GlobalIDField(IDField): +class GlobalIDField(Field): '''The ID of an object''' - required = True + def __init__(self, *args, **kwargs): + super(GlobalIDField, self).__init__(ID(), *args, **kwargs) + self.required = True def contribute_to_class(self, cls, name): from graphene.relay.utils import is_node, is_node_type @@ -96,5 +99,5 @@ class GlobalIDField(IDField): assert in_node, 'GlobalIDField could only be inside a Node, but got %r' % cls super(GlobalIDField, self).contribute_to_class(cls, name) - def resolve(self, instance, args, info): + def resolver(self, instance, args, info): return self.object_type.to_global_id(instance, args, info) diff --git a/tests/relay/test_relay_mutations.py b/graphene/relay/tests/test_mutations.py similarity index 100% rename from tests/relay/test_relay_mutations.py rename to graphene/relay/tests/test_mutations.py diff --git a/tests/relay/test_relayfields.py b/graphene/relay/tests/test_query.py similarity index 95% rename from tests/relay/test_relayfields.py rename to graphene/relay/tests/test_query.py index 44313daf..92e9d6f7 100644 --- a/tests/relay/test_relayfields.py +++ b/graphene/relay/tests/test_query.py @@ -15,7 +15,7 @@ class MyNode(relay.Node): @classmethod def get_node(cls, id): - return MyNode(name='mo') + return MyNode(id=id, name='mo') class Query(graphene.ObjectType): @@ -35,6 +35,7 @@ def test_nodefield_query(): query = ''' query RebelsShipsQuery { myNode(id:"TXlOb2RlOjE=") { + id name }, allMyNodes (customArg:"1") { @@ -52,6 +53,7 @@ def test_nodefield_query(): ''' expected = { 'myNode': { + 'id': 'TXlOb2RlOjE=', 'name': 'mo' }, 'allMyNodes': { diff --git a/tests/relay/test_relay.py b/graphene/relay/tests/test_types.py similarity index 100% rename from tests/relay/test_relay.py rename to graphene/relay/tests/test_types.py diff --git a/tests/contrib_django/test_converter.py b/tests/contrib_django/test_converter.py index 217849ab..699b6ebd 100644 --- a/tests/contrib_django/test_converter.py +++ b/tests/contrib_django/test_converter.py @@ -15,8 +15,9 @@ def assert_conversion(django_field, graphene_field, *args): field = django_field(*args, help_text='Custom Help Text') graphene_type = convert_django_field(field) assert isinstance(graphene_type, graphene_field) - assert graphene_type.description == 'Custom Help Text' - return graphene_type + field = graphene_type.as_field() + assert field.description == 'Custom Help Text' + return field def test_should_unknown_django_field_raise_exception(): @@ -26,86 +27,86 @@ def test_should_unknown_django_field_raise_exception(): def test_should_date_convert_string(): - assert_conversion(models.DateField, graphene.StringField) + assert_conversion(models.DateField, graphene.String) def test_should_char_convert_string(): - assert_conversion(models.CharField, graphene.StringField) + assert_conversion(models.CharField, graphene.String) def test_should_text_convert_string(): - assert_conversion(models.TextField, graphene.StringField) + assert_conversion(models.TextField, graphene.String) def test_should_email_convert_string(): - assert_conversion(models.EmailField, graphene.StringField) + assert_conversion(models.EmailField, graphene.String) def test_should_slug_convert_string(): - assert_conversion(models.SlugField, graphene.StringField) + assert_conversion(models.SlugField, graphene.String) def test_should_url_convert_string(): - assert_conversion(models.URLField, graphene.StringField) + assert_conversion(models.URLField, graphene.String) def test_should_auto_convert_id(): - assert_conversion(models.AutoField, graphene.IDField) + assert_conversion(models.AutoField, graphene.ID) def test_should_positive_integer_convert_int(): - assert_conversion(models.PositiveIntegerField, graphene.IntField) + assert_conversion(models.PositiveIntegerField, graphene.Int) def test_should_positive_small_convert_int(): - assert_conversion(models.PositiveSmallIntegerField, graphene.IntField) + assert_conversion(models.PositiveSmallIntegerField, graphene.Int) def test_should_small_integer_convert_int(): - assert_conversion(models.SmallIntegerField, graphene.IntField) + assert_conversion(models.SmallIntegerField, graphene.Int) def test_should_big_integer_convert_int(): - assert_conversion(models.BigIntegerField, graphene.IntField) + assert_conversion(models.BigIntegerField, graphene.Int) def test_should_integer_convert_int(): - assert_conversion(models.IntegerField, graphene.IntField) + assert_conversion(models.IntegerField, graphene.Int) def test_should_boolean_convert_boolean(): - field = assert_conversion(models.BooleanField, graphene.BooleanField) + field = assert_conversion(models.BooleanField, graphene.Boolean) assert field.required is True def test_should_nullboolean_convert_boolean(): - field = assert_conversion(models.NullBooleanField, graphene.BooleanField) + field = assert_conversion(models.NullBooleanField, graphene.Boolean) assert field.required is False def test_should_float_convert_float(): - assert_conversion(models.FloatField, graphene.FloatField) + assert_conversion(models.FloatField, graphene.Float) def test_should_manytomany_convert_connectionorlist(): graphene_type = convert_django_field(Reporter._meta.local_many_to_many[0]) assert isinstance(graphene_type, ConnectionOrListField) - assert isinstance(graphene_type.field_type, DjangoModelField) - assert graphene_type.field_type.model == Reporter + assert isinstance(graphene_type.type, DjangoModelField) + assert graphene_type.type.model == Reporter def test_should_manytoone_convert_connectionorlist(): graphene_type = convert_django_field(Reporter.articles.related) assert isinstance(graphene_type, ConnectionOrListField) - assert isinstance(graphene_type.field_type, DjangoModelField) - assert graphene_type.field_type.model == Article + assert isinstance(graphene_type.type, DjangoModelField) + assert graphene_type.type.model == Article def test_should_onetoone_convert_model(): field = assert_conversion(models.OneToOneField, DjangoModelField, Article) - assert field.model == Article + assert field.type.model == Article def test_should_foreignkey_convert_model(): field = assert_conversion(models.ForeignKey, DjangoModelField, Article) - assert field.model == Article + assert field.type.model == Article diff --git a/tests/contrib_django/test_types.py b/tests/contrib_django/test_types.py index 94472745..bd864e0a 100644 --- a/tests/contrib_django/test_types.py +++ b/tests/contrib_django/test_types.py @@ -2,7 +2,8 @@ from graphene import Schema from graphene.contrib.django.types import DjangoInterface, DjangoNode -from graphene.core.fields import IntField +from graphene.core.fields import IntField, Field +from graphene.core.types.scalars import String, Int from graphene.relay.fields import GlobalIDField from graphql.core.type import GraphQLInterfaceType, GraphQLObjectType from tests.utils import assert_equal_lists @@ -57,7 +58,8 @@ def test_node_idfield(): def test_node_replacedfield(): idfield = Human._meta.fields_map['pub_date'] - assert isinstance(idfield, IntField) + assert isinstance(idfield, Field) + assert schema.T(idfield).type == schema.T(Int()) def test_interface_resolve_type():