Skip to content

Commit 699aebe

Browse files
committed
Merge pull request #61 from graphql-python/features/classtypes
Refactor ObjectTypes in ClassTypes. Fixed #30
2 parents e78936c + d251a52 commit 699aebe

34 files changed

+924
-657
lines changed

bin/autolinter

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/bin/bash
22

3-
autoflake ./ -r --remove-unused-variables --remove-all-unused-imports --in-place
4-
autopep8 ./ -r --in-place --experimental --aggressive --max-line-length 120
5-
isort -rc .
3+
autoflake ./examples/ ./graphene/ -r --remove-unused-variables --remove-all-unused-imports --in-place
4+
autopep8 ./examples/ ./graphene/ -r --in-place --experimental --aggressive --max-line-length 120
5+
isort -rc ./examples/ ./graphene/

graphene/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@
88
Schema
99
)
1010

11-
from graphene.core.types import (
11+
from graphene.core.classtypes import (
1212
ObjectType,
1313
InputObjectType,
1414
Interface,
1515
Mutation,
16+
Scalar
17+
)
18+
19+
from graphene.core.types import (
1620
BaseType,
1721
LazyType,
1822
Argument,
@@ -59,6 +63,7 @@
5963
'InputObjectType',
6064
'Interface',
6165
'Mutation',
66+
'Scalar',
6267
'Field',
6368
'InputField',
6469
'StringField',

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)
Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
from graphql.core.type import GraphQLInterfaceType, GraphQLObjectType
1+
from graphql.core.type import GraphQLObjectType
22
from mock import patch
3-
from pytest import raises
43

54
from graphene import Schema
6-
from graphene.contrib.django.types import DjangoInterface, DjangoNode
5+
from graphene.contrib.django.types import DjangoNode, DjangoObjectType
76
from graphene.core.fields import Field
87
from graphene.core.types.scalars import Int
98
from graphene.relay.fields import GlobalIDField
@@ -14,7 +13,8 @@
1413
schema = Schema()
1514

1615

17-
class Character(DjangoInterface):
16+
@schema.register
17+
class Character(DjangoObjectType):
1818
'''Character description'''
1919
class Meta:
2020
model = Reporter
@@ -31,7 +31,7 @@ class Meta:
3131

3232

3333
def test_django_interface():
34-
assert DjangoNode._meta.is_interface is True
34+
assert DjangoNode._meta.interface is True
3535

3636

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

4343

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-
5544
def test_djangonode_idfield():
5645
idfield = DjangoNode._meta.fields_map['id']
5746
assert isinstance(idfield, GlobalIDField)
@@ -68,32 +57,21 @@ def test_node_replacedfield():
6857
assert schema.T(idfield).type == schema.T(Int())
6958

7059

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():
60+
def test_objecttype_init_none():
7761
h = Human()
7862
assert h._root is None
7963

8064

81-
def test_interface_objecttype_init_good():
65+
def test_objecttype_init_good():
8266
instance = Article()
8367
h = Human(instance)
8468
assert h._root == instance
8569

8670

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-
9371
def test_object_type():
9472
object_type = schema.T(Human)
9573
Human._meta.fields_map
96-
assert Human._meta.is_interface is False
74+
assert Human._meta.interface is False
9775
assert isinstance(object_type, GraphQLObjectType)
9876
assert_equal_lists(
9977
object_type.get_fields().keys(),
@@ -103,5 +81,5 @@ def test_object_type():
10381

10482

10583
def test_node_notinterface():
106-
assert Human._meta.is_interface is False
84+
assert Human._meta.interface is False
10785
assert DjangoNode in Human._meta.interfaces

graphene/contrib/django/types.py

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
1+
import inspect
2+
13
import six
4+
from django.db import models
25

3-
from ...core.types import BaseObjectType, ObjectTypeMeta
4-
from ...relay.fields import GlobalIDField
5-
from ...relay.types import BaseNode, Connection
6+
from ...core.classtypes.objecttype import ObjectType, ObjectTypeMeta
7+
from ...relay.types import Connection, Node, NodeMeta
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,24 @@ 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+
51+
class Meta:
52+
abstract = True
4053

4154
def __init__(self, _root=None):
4255
if _root:
@@ -63,12 +76,9 @@ def __getattr__(self, attr):
6376

6477
class DjangoObjectType(six.with_metaclass(
6578
DjangoObjectTypeMeta, InstanceObjectType)):
66-
pass
6779

68-
69-
class DjangoInterface(six.with_metaclass(
70-
DjangoObjectTypeMeta, InstanceObjectType)):
71-
pass
80+
class Meta:
81+
abstract = True
7282

7383

7484
class DjangoConnection(Connection):
@@ -79,8 +89,21 @@ def from_list(cls, iterable, *args, **kwargs):
7989
return super(DjangoConnection, cls).from_list(iterable, *args, **kwargs)
8090

8191

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

85108
@classmethod
86109
def get_node(cls, id, info=None):

graphene/core/classtypes/__init__.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from .inputobjecttype import InputObjectType
2+
from .interface import Interface
3+
from .mutation import Mutation
4+
from .objecttype import ObjectType
5+
from .options import Options
6+
from .scalar import Scalar
7+
from .uniontype import UnionType
8+
9+
__all__ = [
10+
'InputObjectType',
11+
'Interface',
12+
'Mutation',
13+
'ObjectType',
14+
'Options',
15+
'Scalar',
16+
'UnionType']

0 commit comments

Comments
 (0)