Improved classtypes django support

This commit is contained in:
Syrus Akbary 2015-12-02 23:36:51 -08:00
parent 5b3000f734
commit f5837ac4f3
5 changed files with 53 additions and 74 deletions

View File

@ -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']

View File

@ -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)

View File

@ -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

View File

@ -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):

View File

@ -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