Skip to content

Commit f5837ac

Browse files
committed
Improved classtypes django support
1 parent 5b3000f commit f5837ac

File tree

5 files changed

+53
-74
lines changed

5 files changed

+53
-74
lines changed

graphene/contrib/django/__init__.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
from graphene.contrib.django.types import (
22
DjangoConnection,
33
DjangoObjectType,
4-
DjangoInterface,
54
DjangoNode
65
)
76
from graphene.contrib.django.fields import (
87
DjangoConnectionField,
98
DjangoModelField
109
)
1110

12-
__all__ = ['DjangoObjectType', 'DjangoInterface', 'DjangoNode',
13-
'DjangoConnection', 'DjangoConnectionField', 'DjangoModelField']
11+
__all__ = ['DjangoObjectType', 'DjangoNode', 'DjangoConnection',
12+
'DjangoConnectionField', 'DjangoModelField']

graphene/contrib/django/options.py

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,15 @@
1-
import inspect
2-
3-
from django.db import models
4-
5-
from ...core.options import Options
1+
from ...core.classtypes.objecttype import ObjectTypeOptions
62
from ...relay.types import Node
73
from ...relay.utils import is_node
84

95
VALID_ATTRS = ('model', 'only_fields', 'exclude_fields')
106

117

12-
def is_base(cls):
13-
from graphene.contrib.django.types import DjangoObjectType
14-
return DjangoObjectType in cls.__bases__
15-
16-
17-
class DjangoOptions(Options):
8+
class DjangoOptions(ObjectTypeOptions):
189

1910
def __init__(self, *args, **kwargs):
20-
self.model = None
2111
super(DjangoOptions, self).__init__(*args, **kwargs)
12+
self.model = None
2213
self.valid_attrs += VALID_ATTRS
2314
self.only_fields = None
2415
self.exclude_fields = []
@@ -28,11 +19,3 @@ def contribute_to_class(self, cls, name):
2819
if is_node(cls):
2920
self.exclude_fields = list(self.exclude_fields) + ['id']
3021
self.interfaces.append(Node)
31-
if not is_node(cls) and not is_base(cls):
32-
return
33-
if not self.model:
34-
raise Exception(
35-
'Django ObjectType %s must have a model in the Meta class attr' %
36-
cls)
37-
elif not inspect.isclass(self.model) or not issubclass(self.model, models.Model):
38-
raise Exception('Provided model in %s is not a Django model' % cls)

graphene/contrib/django/tests/test_types.py

Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from pytest import raises
44

55
from graphene import Schema
6-
from graphene.contrib.django.types import DjangoInterface, DjangoNode
6+
from graphene.contrib.django.types import DjangoNode, DjangoObjectType
77
from graphene.core.fields import Field
88
from graphene.core.types.scalars import Int
99
from graphene.relay.fields import GlobalIDField
@@ -14,7 +14,8 @@
1414
schema = Schema()
1515

1616

17-
class Character(DjangoInterface):
17+
@schema.register
18+
class Character(DjangoObjectType):
1819
'''Character description'''
1920
class Meta:
2021
model = Reporter
@@ -31,7 +32,7 @@ class Meta:
3132

3233

3334
def test_django_interface():
34-
assert DjangoNode._meta.is_interface is True
35+
assert DjangoNode._meta.interface is True
3536

3637

3738
@patch('graphene.contrib.django.tests.models.Article.objects.get', return_value=Article(id=1))
@@ -41,17 +42,6 @@ def test_django_get_node(get):
4142
assert human.id == 1
4243

4344

44-
def test_pseudo_interface_registered():
45-
object_type = schema.T(Character)
46-
assert Character._meta.is_interface is True
47-
assert isinstance(object_type, GraphQLInterfaceType)
48-
assert Character._meta.model == Reporter
49-
assert_equal_lists(
50-
object_type.get_fields().keys(),
51-
['articles', 'firstName', 'lastName', 'email', 'pets', 'id']
52-
)
53-
54-
5545
def test_djangonode_idfield():
5646
idfield = DjangoNode._meta.fields_map['id']
5747
assert isinstance(idfield, GlobalIDField)
@@ -68,32 +58,21 @@ def test_node_replacedfield():
6858
assert schema.T(idfield).type == schema.T(Int())
6959

7060

71-
def test_interface_resolve_type():
72-
resolve_type = Character._resolve_type(schema, Human())
73-
assert isinstance(resolve_type, GraphQLObjectType)
74-
75-
76-
def test_interface_objecttype_init_none():
61+
def test_objecttype_init_none():
7762
h = Human()
7863
assert h._root is None
7964

8065

81-
def test_interface_objecttype_init_good():
66+
def test_objecttype_init_good():
8267
instance = Article()
8368
h = Human(instance)
8469
assert h._root == instance
8570

8671

87-
def test_interface_objecttype_init_unexpected():
88-
with raises(AssertionError) as excinfo:
89-
Human(object())
90-
assert str(excinfo.value) == "Human received a non-compatible instance (object) when expecting Article"
91-
92-
9372
def test_object_type():
9473
object_type = schema.T(Human)
9574
Human._meta.fields_map
96-
assert Human._meta.is_interface is False
75+
assert Human._meta.interface is False
9776
assert isinstance(object_type, GraphQLObjectType)
9877
assert_equal_lists(
9978
object_type.get_fields().keys(),
@@ -103,5 +82,5 @@ def test_object_type():
10382

10483

10584
def test_node_notinterface():
106-
assert Human._meta.is_interface is False
85+
assert Human._meta.interface is False
10786
assert DjangoNode in Human._meta.interfaces

graphene/contrib/django/types.py

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
11
import six
2+
import inspect
23

3-
from ...core.types import BaseObjectType, ObjectTypeMeta
4-
from ...relay.fields import GlobalIDField
5-
from ...relay.types import BaseNode, Connection
4+
from django.db import models
5+
6+
from ...core.classtypes.objecttype import ObjectTypeMeta, ObjectType
7+
from ...relay.types import Node, NodeMeta, Connection
68
from .converter import convert_django_field
79
from .options import DjangoOptions
810
from .utils import get_reverse_fields, maybe_queryset
911

1012

1113
class DjangoObjectTypeMeta(ObjectTypeMeta):
12-
options_cls = DjangoOptions
13-
14-
def is_interface(cls, parents):
15-
return DjangoInterface in parents
14+
options_class = DjangoOptions
1615

17-
def add_extra_fields(cls):
18-
if not cls._meta.model:
19-
return
16+
def construct_fields(cls):
2017
only_fields = cls._meta.only_fields
2118
reverse_fields = get_reverse_fields(cls._meta.model)
2219
all_fields = sorted(list(cls._meta.model._meta.fields) +
@@ -35,8 +32,23 @@ def add_extra_fields(cls):
3532
converted_field = convert_django_field(field)
3633
cls.add_to_class(field.name, converted_field)
3734

35+
def construct(cls, *args, **kwargs):
36+
cls = super(DjangoObjectTypeMeta, cls).construct(*args, **kwargs)
37+
if not cls._meta.abstract:
38+
if not cls._meta.model:
39+
raise Exception(
40+
'Django ObjectType %s must have a model in the Meta class attr' %
41+
cls)
42+
elif not inspect.isclass(cls._meta.model) or not issubclass(cls._meta.model, models.Model):
43+
raise Exception('Provided model in %s is not a Django model' % cls)
44+
45+
cls.construct_fields()
46+
return cls
47+
3848

39-
class InstanceObjectType(BaseObjectType):
49+
class InstanceObjectType(ObjectType):
50+
class Meta:
51+
abstract = True
4052

4153
def __init__(self, _root=None):
4254
if _root:
@@ -63,12 +75,8 @@ def __getattr__(self, attr):
6375

6476
class DjangoObjectType(six.with_metaclass(
6577
DjangoObjectTypeMeta, InstanceObjectType)):
66-
pass
67-
68-
69-
class DjangoInterface(six.with_metaclass(
70-
DjangoObjectTypeMeta, InstanceObjectType)):
71-
pass
78+
class Meta:
79+
abstract = True
7280

7381

7482
class DjangoConnection(Connection):
@@ -79,8 +87,19 @@ def from_list(cls, iterable, *args, **kwargs):
7987
return super(DjangoConnection, cls).from_list(iterable, *args, **kwargs)
8088

8189

82-
class DjangoNode(BaseNode, DjangoInterface):
83-
id = GlobalIDField()
90+
class DjangoNodeMeta(DjangoObjectTypeMeta, NodeMeta):
91+
pass
92+
93+
94+
class NodeInstance(Node, InstanceObjectType):
95+
class Meta:
96+
abstract = True
97+
98+
99+
class DjangoNode(six.with_metaclass(
100+
DjangoNodeMeta, NodeInstance)):
101+
class Meta:
102+
abstract = True
84103

85104
@classmethod
86105
def get_node(cls, id, info=None):

graphene/core/classtypes/base.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def extend_fields(cls, bases):
8787
field_names = {f.name: f for f in new_fields}
8888

8989
for base in bases:
90-
if not issubclass(base, FieldsClassType):
90+
if not isinstance(base, FieldsClassTypeMeta):
9191
continue
9292

9393
parent_fields = base._meta.local_fields
@@ -110,8 +110,7 @@ def extend_fields(cls, bases):
110110

111111
def construct(cls, bases, attrs):
112112
cls = super(FieldsClassTypeMeta, cls).construct(bases, attrs)
113-
if not cls._meta.abstract:
114-
cls.extend_fields(bases)
113+
cls.extend_fields(bases)
115114
return cls
116115

117116

0 commit comments

Comments
 (0)