mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-22 09:36:44 +03:00
Improved classtypes django support
This commit is contained in:
parent
5b3000f734
commit
f5837ac4f3
|
@ -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']
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user