diff --git a/graphene/contrib/django/__init__.py b/graphene/contrib/django/__init__.py index ec39b7f9..11720f9f 100644 --- a/graphene/contrib/django/__init__.py +++ b/graphene/contrib/django/__init__.py @@ -1,7 +1,6 @@ from graphene.contrib.django.types import ( DjangoConnection, DjangoObjectType, - DjangoInterface, DjangoNode ) from graphene.contrib.django.fields import ( @@ -9,5 +8,5 @@ from graphene.contrib.django.fields import ( DjangoModelField ) -__all__ = ['DjangoObjectType', 'DjangoInterface', 'DjangoNode', - 'DjangoConnection', 'DjangoConnectionField', 'DjangoModelField'] +__all__ = ['DjangoObjectType', 'DjangoNode', 'DjangoConnection', + 'DjangoConnectionField', 'DjangoModelField'] diff --git a/graphene/contrib/django/options.py b/graphene/contrib/django/options.py index 812e2b03..61dd37a3 100644 --- a/graphene/contrib/django/options.py +++ b/graphene/contrib/django/options.py @@ -1,24 +1,15 @@ -import inspect - -from django.db import models - -from ...core.options import Options +from ...core.classtypes.objecttype import ObjectTypeOptions from ...relay.types import Node from ...relay.utils import is_node VALID_ATTRS = ('model', 'only_fields', 'exclude_fields') -def is_base(cls): - from graphene.contrib.django.types import DjangoObjectType - return DjangoObjectType in cls.__bases__ - - -class DjangoOptions(Options): +class DjangoOptions(ObjectTypeOptions): def __init__(self, *args, **kwargs): - self.model = None super(DjangoOptions, self).__init__(*args, **kwargs) + self.model = None self.valid_attrs += VALID_ATTRS self.only_fields = None self.exclude_fields = [] @@ -28,11 +19,3 @@ class DjangoOptions(Options): if is_node(cls): self.exclude_fields = list(self.exclude_fields) + ['id'] self.interfaces.append(Node) - if not is_node(cls) and not is_base(cls): - 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) diff --git a/graphene/contrib/django/tests/test_types.py b/graphene/contrib/django/tests/test_types.py index 4d709580..959a7d45 100644 --- a/graphene/contrib/django/tests/test_types.py +++ b/graphene/contrib/django/tests/test_types.py @@ -3,7 +3,7 @@ from mock import patch from pytest import raises from graphene import Schema -from graphene.contrib.django.types import DjangoInterface, DjangoNode +from graphene.contrib.django.types import DjangoNode, DjangoObjectType from graphene.core.fields import Field from graphene.core.types.scalars import Int from graphene.relay.fields import GlobalIDField @@ -14,7 +14,8 @@ from .models import Article, Reporter schema = Schema() -class Character(DjangoInterface): +@schema.register +class Character(DjangoObjectType): '''Character description''' class Meta: model = Reporter @@ -31,7 +32,7 @@ class Human(DjangoNode): def test_django_interface(): - assert DjangoNode._meta.is_interface is True + assert DjangoNode._meta.interface is True @patch('graphene.contrib.django.tests.models.Article.objects.get', return_value=Article(id=1)) @@ -41,17 +42,6 @@ def test_django_get_node(get): assert human.id == 1 -def test_pseudo_interface_registered(): - object_type = schema.T(Character) - assert Character._meta.is_interface is True - assert isinstance(object_type, GraphQLInterfaceType) - assert Character._meta.model == Reporter - assert_equal_lists( - object_type.get_fields().keys(), - ['articles', 'firstName', 'lastName', 'email', 'pets', 'id'] - ) - - def test_djangonode_idfield(): idfield = DjangoNode._meta.fields_map['id'] assert isinstance(idfield, GlobalIDField) @@ -68,32 +58,21 @@ def test_node_replacedfield(): assert schema.T(idfield).type == schema.T(Int()) -def test_interface_resolve_type(): - resolve_type = Character._resolve_type(schema, Human()) - assert isinstance(resolve_type, GraphQLObjectType) - - -def test_interface_objecttype_init_none(): +def test_objecttype_init_none(): h = Human() assert h._root is None -def test_interface_objecttype_init_good(): +def test_objecttype_init_good(): instance = Article() h = Human(instance) assert h._root == instance -def test_interface_objecttype_init_unexpected(): - with raises(AssertionError) as excinfo: - Human(object()) - assert str(excinfo.value) == "Human received a non-compatible instance (object) when expecting Article" - - def test_object_type(): object_type = schema.T(Human) Human._meta.fields_map - assert Human._meta.is_interface is False + assert Human._meta.interface is False assert isinstance(object_type, GraphQLObjectType) assert_equal_lists( object_type.get_fields().keys(), @@ -103,5 +82,5 @@ def test_object_type(): def test_node_notinterface(): - assert Human._meta.is_interface is False + assert Human._meta.interface is False assert DjangoNode in Human._meta.interfaces diff --git a/graphene/contrib/django/types.py b/graphene/contrib/django/types.py index b5097c72..0d7b75fe 100644 --- a/graphene/contrib/django/types.py +++ b/graphene/contrib/django/types.py @@ -1,22 +1,19 @@ import six +import inspect -from ...core.types import BaseObjectType, ObjectTypeMeta -from ...relay.fields import GlobalIDField -from ...relay.types import BaseNode, Connection +from django.db import models + +from ...core.classtypes.objecttype import ObjectTypeMeta, ObjectType +from ...relay.types import Node, NodeMeta, Connection from .converter import convert_django_field from .options import DjangoOptions from .utils import get_reverse_fields, maybe_queryset class DjangoObjectTypeMeta(ObjectTypeMeta): - options_cls = DjangoOptions + options_class = DjangoOptions - def is_interface(cls, parents): - return DjangoInterface in parents - - def add_extra_fields(cls): - if not cls._meta.model: - return + def construct_fields(cls): only_fields = cls._meta.only_fields reverse_fields = get_reverse_fields(cls._meta.model) all_fields = sorted(list(cls._meta.model._meta.fields) + @@ -35,8 +32,23 @@ class DjangoObjectTypeMeta(ObjectTypeMeta): converted_field = convert_django_field(field) cls.add_to_class(field.name, converted_field) + def construct(cls, *args, **kwargs): + cls = super(DjangoObjectTypeMeta, cls).construct(*args, **kwargs) + if not cls._meta.abstract: + if not cls._meta.model: + raise Exception( + 'Django ObjectType %s must have a model in the Meta class attr' % + cls) + elif not inspect.isclass(cls._meta.model) or not issubclass(cls._meta.model, models.Model): + raise Exception('Provided model in %s is not a Django model' % cls) -class InstanceObjectType(BaseObjectType): + cls.construct_fields() + return cls + + +class InstanceObjectType(ObjectType): + class Meta: + abstract = True def __init__(self, _root=None): if _root: @@ -63,12 +75,8 @@ class InstanceObjectType(BaseObjectType): class DjangoObjectType(six.with_metaclass( DjangoObjectTypeMeta, InstanceObjectType)): - pass - - -class DjangoInterface(six.with_metaclass( - DjangoObjectTypeMeta, InstanceObjectType)): - pass + class Meta: + abstract = True class DjangoConnection(Connection): @@ -79,8 +87,19 @@ class DjangoConnection(Connection): return super(DjangoConnection, cls).from_list(iterable, *args, **kwargs) -class DjangoNode(BaseNode, DjangoInterface): - id = GlobalIDField() +class DjangoNodeMeta(DjangoObjectTypeMeta, NodeMeta): + pass + + +class NodeInstance(Node, InstanceObjectType): + class Meta: + abstract = True + + +class DjangoNode(six.with_metaclass( + DjangoNodeMeta, NodeInstance)): + class Meta: + abstract = True @classmethod def get_node(cls, id, info=None): diff --git a/graphene/core/classtypes/base.py b/graphene/core/classtypes/base.py index c6d4dc58..52588fc8 100644 --- a/graphene/core/classtypes/base.py +++ b/graphene/core/classtypes/base.py @@ -87,7 +87,7 @@ class FieldsClassTypeMeta(ClassTypeMeta): field_names = {f.name: f for f in new_fields} for base in bases: - if not issubclass(base, FieldsClassType): + if not isinstance(base, FieldsClassTypeMeta): continue parent_fields = base._meta.local_fields @@ -110,8 +110,7 @@ class FieldsClassTypeMeta(ClassTypeMeta): def construct(cls, bases, attrs): cls = super(FieldsClassTypeMeta, cls).construct(bases, attrs) - if not cls._meta.abstract: - cls.extend_fields(bases) + cls.extend_fields(bases) return cls