mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-22 17:46:57 +03:00
Improved Django integration.
This commit is contained in:
parent
dbc981c6d2
commit
26f364de0b
|
@ -1,11 +1,11 @@
|
|||
from django.db import models
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
from graphene import Enum, List, ID, Boolean, Float, Int, String, Field, NonNull
|
||||
from graphene import Enum, List, ID, Boolean, Float, Int, String, Field, NonNull, Field, Dynamic
|
||||
from graphene.types.json import JSONString
|
||||
from graphene.types.datetime import DateTime
|
||||
from graphene.utils.str_converters import to_const
|
||||
from graphene.relay import Node
|
||||
from graphene.relay import is_node
|
||||
|
||||
from .compat import (ArrayField, HStoreField, JSONField, RangeField,
|
||||
RelatedObject, UUIDField)
|
||||
|
@ -92,7 +92,11 @@ def convert_date_to_string(field, registry=None):
|
|||
@convert_django_field.register(models.OneToOneRel)
|
||||
def convert_onetoone_field_to_djangomodel(field, registry=None):
|
||||
model = get_related_model(field)
|
||||
return Field(registry.get_type_for_model(model))
|
||||
|
||||
def dynamic_type():
|
||||
return Field(registry.get_type_for_model(model))
|
||||
|
||||
return Dynamic(dynamic_type)
|
||||
|
||||
|
||||
@convert_django_field.register(models.ManyToManyField)
|
||||
|
@ -100,31 +104,46 @@ def convert_onetoone_field_to_djangomodel(field, registry=None):
|
|||
@convert_django_field.register(models.ManyToOneRel)
|
||||
def convert_field_to_list_or_connection(field, registry=None):
|
||||
model = get_related_model(field)
|
||||
_type = registry.get_type_for_model(model)
|
||||
if not _type:
|
||||
return
|
||||
|
||||
if issubclass(_type, Node):
|
||||
return get_connection_field(_type)
|
||||
return Field(List(_type))
|
||||
def dynamic_type():
|
||||
_type = registry.get_type_for_model(model)
|
||||
if not _type:
|
||||
return
|
||||
|
||||
if is_node(_type):
|
||||
return get_connection_field(_type)
|
||||
return Field(List(_type))
|
||||
|
||||
return Dynamic(dynamic_type)
|
||||
|
||||
|
||||
# For Django 1.6
|
||||
@convert_django_field.register(RelatedObject)
|
||||
def convert_relatedfield_to_djangomodel(field, registry=None):
|
||||
model = field.model
|
||||
_type = registry.get_type_for_model(model)
|
||||
if issubclass(_type, Node):
|
||||
return get_connection_field(_type)
|
||||
return List(_type)
|
||||
|
||||
def dynamic_type():
|
||||
_type = registry.get_type_for_model(model)
|
||||
if not _type:
|
||||
return
|
||||
|
||||
if is_node(_type):
|
||||
return get_connection_field(_type)
|
||||
return Field(List(_type))
|
||||
|
||||
return Dynamic(dynamic_type)
|
||||
|
||||
|
||||
@convert_django_field.register(models.OneToOneField)
|
||||
@convert_django_field.register(models.ForeignKey)
|
||||
def convert_field_to_djangomodel(field, registry=None):
|
||||
model = get_related_model(field)
|
||||
_type = registry.get_type_for_model(model)
|
||||
return Field(_type, description=field.help_text)
|
||||
|
||||
def dynamic_type():
|
||||
_type = registry.get_type_for_model(model)
|
||||
return Field(_type, description=field.help_text)
|
||||
|
||||
return Dynamic(dynamic_type)
|
||||
|
||||
|
||||
@convert_django_field.register(ArrayField)
|
||||
|
|
|
@ -21,7 +21,7 @@ class DjangoConnectionField(ConnectionField):
|
|||
return self.model._default_manager
|
||||
|
||||
def default_resolver(self, root, args, context, info):
|
||||
return getattr(root, self.source or self.attname, self.get_manager())
|
||||
return getattr(root, self.source, self.get_manager())
|
||||
|
||||
@staticmethod
|
||||
def connection_resolver(resolver, connection, root, args, context, info):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from ..fields import DjangoConnectionField
|
||||
from .utils import get_filtering_args_from_filterset, get_filterset_class
|
||||
|
||||
from graphene.types.argument import to_arguments
|
||||
# from graphene.types.argument import to_arguments
|
||||
|
||||
class DjangoFilterConnectionField(DjangoConnectionField):
|
||||
|
||||
|
@ -19,7 +19,8 @@ class DjangoFilterConnectionField(DjangoConnectionField):
|
|||
self.filterset_class = get_filterset_class(filterset_class, **meta)
|
||||
self.filtering_args = get_filtering_args_from_filterset(self.filterset_class, type)
|
||||
kwargs.setdefault('args', {})
|
||||
kwargs['args'].update(to_arguments(self.filtering_args))
|
||||
kwargs['args'].update(self.filtering_args)
|
||||
# kwargs['args'].update(to_arguments(self.filtering_args))
|
||||
super(DjangoFilterConnectionField, self).__init__(type, *args, **kwargs)
|
||||
|
||||
def get_queryset(self, qs, args, info):
|
||||
|
|
|
@ -21,22 +21,25 @@ else:
|
|||
pytestmark.append(pytest.mark.django_db)
|
||||
|
||||
|
||||
class ArticleNode(DjangoNode, DjangoObjectType):
|
||||
class ArticleNode(DjangoObjectType):
|
||||
|
||||
class Meta:
|
||||
model = Article
|
||||
interfaces = (DjangoNode, )
|
||||
|
||||
|
||||
class ReporterNode(DjangoNode, DjangoObjectType):
|
||||
class ReporterNode(DjangoObjectType):
|
||||
|
||||
class Meta:
|
||||
model = Reporter
|
||||
interfaces = (DjangoNode, )
|
||||
|
||||
|
||||
class PetNode(DjangoNode, DjangoObjectType):
|
||||
class PetNode(DjangoObjectType):
|
||||
|
||||
class Meta:
|
||||
model = Pet
|
||||
interfaces = (DjangoNode, )
|
||||
|
||||
# schema = Schema()
|
||||
|
||||
|
@ -49,7 +52,7 @@ def get_args(field):
|
|||
|
||||
|
||||
def assert_arguments(field, *arguments):
|
||||
ignore = ('after', 'before', 'first', 'last', 'orderBy')
|
||||
ignore = ('after', 'before', 'first', 'last', 'order_by')
|
||||
args = get_args(field)
|
||||
actual = [
|
||||
name
|
||||
|
@ -65,21 +68,21 @@ def assert_arguments(field, *arguments):
|
|||
|
||||
def assert_orderable(field):
|
||||
args = get_args(field)
|
||||
assert 'orderBy' in args, \
|
||||
assert 'order_by' in args, \
|
||||
'Field cannot be ordered'
|
||||
|
||||
|
||||
def assert_not_orderable(field):
|
||||
args = get_args(field)
|
||||
assert 'orderBy' not in args, \
|
||||
assert 'order_by' not in args, \
|
||||
'Field can be ordered'
|
||||
|
||||
|
||||
def test_filter_explicit_filterset_arguments():
|
||||
field = DjangoFilterConnectionField(ArticleNode, filterset_class=ArticleFilter)
|
||||
assert_arguments(field,
|
||||
'headline', 'headline_Icontains',
|
||||
'pubDate', 'pubDate_Gt', 'pubDate_Lt',
|
||||
'headline', 'headline__icontains',
|
||||
'pub_date', 'pub_date__gt', 'pub_date__lt',
|
||||
'reporter',
|
||||
)
|
||||
|
||||
|
@ -87,7 +90,7 @@ def test_filter_explicit_filterset_arguments():
|
|||
def test_filter_shortcut_filterset_arguments_list():
|
||||
field = DjangoFilterConnectionField(ArticleNode, fields=['pub_date', 'reporter'])
|
||||
assert_arguments(field,
|
||||
'pubDate',
|
||||
'pub_date',
|
||||
'reporter',
|
||||
)
|
||||
|
||||
|
@ -98,7 +101,7 @@ def test_filter_shortcut_filterset_arguments_dict():
|
|||
'reporter': ['exact'],
|
||||
})
|
||||
assert_arguments(field,
|
||||
'headline', 'headline_Icontains',
|
||||
'headline', 'headline__icontains',
|
||||
'reporter',
|
||||
)
|
||||
|
||||
|
@ -131,30 +134,33 @@ def test_filter_shortcut_filterset_extra_meta():
|
|||
|
||||
|
||||
def test_filter_filterset_information_on_meta():
|
||||
class ReporterFilterNode(DjangoNode, DjangoObjectType):
|
||||
class ReporterFilterNode(DjangoObjectType):
|
||||
|
||||
class Meta:
|
||||
model = Reporter
|
||||
interfaces = (DjangoNode, )
|
||||
filter_fields = ['first_name', 'articles']
|
||||
filter_order_by = True
|
||||
|
||||
field = DjangoFilterConnectionField(ReporterFilterNode)
|
||||
assert_arguments(field, 'firstName', 'articles')
|
||||
assert_arguments(field, 'first_name', 'articles')
|
||||
assert_orderable(field)
|
||||
|
||||
|
||||
def test_filter_filterset_information_on_meta_related():
|
||||
class ReporterFilterNode(DjangoNode, DjangoObjectType):
|
||||
class ReporterFilterNode(DjangoObjectType):
|
||||
|
||||
class Meta:
|
||||
model = Reporter
|
||||
interfaces = (DjangoNode, )
|
||||
filter_fields = ['first_name', 'articles']
|
||||
filter_order_by = True
|
||||
|
||||
class ArticleFilterNode(DjangoNode, DjangoObjectType):
|
||||
class ArticleFilterNode(DjangoObjectType):
|
||||
|
||||
class Meta:
|
||||
model = Article
|
||||
interfaces = (DjangoNode, )
|
||||
filter_fields = ['headline', 'reporter']
|
||||
filter_order_by = True
|
||||
|
||||
|
@ -165,22 +171,24 @@ def test_filter_filterset_information_on_meta_related():
|
|||
article = Field(ArticleFilterNode)
|
||||
|
||||
schema = Schema(query=Query)
|
||||
articles_field = ReporterFilterNode._meta.graphql_type.get_fields()['articles']
|
||||
articles_field = ReporterFilterNode._meta.fields['articles'].get_type()
|
||||
assert_arguments(articles_field, 'headline', 'reporter')
|
||||
assert_orderable(articles_field)
|
||||
|
||||
|
||||
def test_filter_filterset_related_results():
|
||||
class ReporterFilterNode(DjangoNode, DjangoObjectType):
|
||||
class ReporterFilterNode(DjangoObjectType):
|
||||
|
||||
class Meta:
|
||||
model = Reporter
|
||||
interfaces = (DjangoNode, )
|
||||
filter_fields = ['first_name', 'articles']
|
||||
filter_order_by = True
|
||||
|
||||
class ArticleFilterNode(DjangoNode, DjangoObjectType):
|
||||
class ArticleFilterNode(DjangoObjectType):
|
||||
|
||||
class Meta:
|
||||
interfaces = (DjangoNode, )
|
||||
model = Article
|
||||
filter_fields = ['headline', 'reporter']
|
||||
filter_order_by = True
|
||||
|
|
|
@ -5,20 +5,22 @@ from ..types import DjangoNode, DjangoObjectType
|
|||
from .models import Article, Reporter
|
||||
|
||||
|
||||
class Character(DjangoNode, DjangoObjectType):
|
||||
class Character(DjangoObjectType):
|
||||
|
||||
class Meta:
|
||||
model = Reporter
|
||||
interfaces = (DjangoNode, )
|
||||
|
||||
def get_node(self, id, context, info):
|
||||
pass
|
||||
|
||||
|
||||
class Human(DjangoNode, DjangoObjectType):
|
||||
class Human(DjangoObjectType):
|
||||
raises = graphene.String()
|
||||
|
||||
class Meta:
|
||||
model = Article
|
||||
interfaces = (DjangoNode, )
|
||||
|
||||
def resolve_raises(self, *args):
|
||||
raise Exception("This field should raise exception")
|
||||
|
|
|
@ -7,7 +7,6 @@ import graphene
|
|||
from graphene.relay import Node, ConnectionField
|
||||
from graphene.types.datetime import DateTime
|
||||
from graphene.types.json import JSONString
|
||||
from graphene.utils.get_graphql_type import get_graphql_type
|
||||
# from graphene.core.types.custom_scalars import DateTime, JSONString
|
||||
|
||||
from ..compat import (ArrayField, HStoreField, JSONField, MissingType,
|
||||
|
@ -22,7 +21,7 @@ def assert_conversion(django_field, graphene_field, *args, **kwargs):
|
|||
field = django_field(help_text='Custom Help Text', *args, **kwargs)
|
||||
graphene_type = convert_django_field(field)
|
||||
assert isinstance(graphene_type, graphene_field)
|
||||
field = graphene_type.as_field()
|
||||
field = graphene_type.Field()
|
||||
assert field.description == 'Custom Help Text'
|
||||
return field
|
||||
|
||||
|
@ -95,7 +94,7 @@ def test_should_integer_convert_int():
|
|||
|
||||
def test_should_boolean_convert_boolean():
|
||||
field = assert_conversion(models.BooleanField, graphene.NonNull)
|
||||
assert field.type.of_type == get_graphql_type(graphene.Boolean)
|
||||
assert field.type.of_type == graphene.Boolean
|
||||
|
||||
|
||||
def test_should_nullboolean_convert_boolean():
|
||||
|
@ -116,7 +115,7 @@ def test_field_with_choices_convert_enum():
|
|||
|
||||
graphene_type = convert_django_field_with_choices(field)
|
||||
assert isinstance(graphene_type, graphene.Enum)
|
||||
assert graphene_type._meta.graphql_type.name == 'TranslatedModelLanguage'
|
||||
assert graphene_type._meta.name == 'TranslatedModelLanguage'
|
||||
assert graphene_type._meta.enum.__members__['SPANISH'].value == 'es'
|
||||
assert graphene_type._meta.enum.__members__['ENGLISH'].value == 'en'
|
||||
|
||||
|
@ -159,8 +158,8 @@ def test_should_float_convert_float():
|
|||
|
||||
def test_should_manytomany_convert_connectionorlist():
|
||||
registry = Registry()
|
||||
graphene_field = convert_django_field(Reporter._meta.local_many_to_many[0], registry)
|
||||
assert not graphene_field
|
||||
dynamic_field = convert_django_field(Reporter._meta.local_many_to_many[0], registry)
|
||||
assert not dynamic_field.get_type()
|
||||
|
||||
|
||||
def test_should_manytomany_convert_connectionorlist_list():
|
||||
|
@ -169,19 +168,24 @@ def test_should_manytomany_convert_connectionorlist_list():
|
|||
model = Reporter
|
||||
|
||||
graphene_field = convert_django_field(Reporter._meta.local_many_to_many[0], A._meta.registry)
|
||||
assert isinstance(graphene_field, graphene.Field)
|
||||
assert isinstance(graphene_field.type, graphene.List)
|
||||
assert graphene_field.type.of_type == get_graphql_type(A)
|
||||
assert isinstance(graphene_field, graphene.Dynamic)
|
||||
dynamic_field = graphene_field.get_type()
|
||||
assert isinstance(dynamic_field, graphene.Field)
|
||||
assert isinstance(dynamic_field.type, graphene.List)
|
||||
assert dynamic_field.type.of_type == A
|
||||
|
||||
|
||||
def test_should_manytomany_convert_connectionorlist_connection():
|
||||
class A(DjangoNode, DjangoObjectType):
|
||||
class A(DjangoObjectType):
|
||||
class Meta:
|
||||
model = Reporter
|
||||
interfaces = (DjangoNode, )
|
||||
|
||||
graphene_field = convert_django_field(Reporter._meta.local_many_to_many[0], A._meta.registry)
|
||||
assert isinstance(graphene_field, ConnectionField)
|
||||
assert graphene_field.type == get_graphql_type(A.get_default_connection())
|
||||
assert isinstance(graphene_field, graphene.Dynamic)
|
||||
dynamic_field = graphene_field.get_type()
|
||||
assert isinstance(dynamic_field, ConnectionField)
|
||||
assert dynamic_field.type == A.Connection
|
||||
|
||||
|
||||
def test_should_manytoone_convert_connectionorlist():
|
||||
|
@ -194,9 +198,11 @@ def test_should_manytoone_convert_connectionorlist():
|
|||
model = Article
|
||||
|
||||
graphene_field = convert_django_field(related, A._meta.registry)
|
||||
assert isinstance(graphene_field, graphene.Field)
|
||||
assert isinstance(graphene_field.type, graphene.List)
|
||||
assert graphene_field.type.of_type == get_graphql_type(A)
|
||||
assert isinstance(graphene_field, graphene.Dynamic)
|
||||
dynamic_field = graphene_field.get_type()
|
||||
assert isinstance(dynamic_field, graphene.Field)
|
||||
assert isinstance(dynamic_field.type, graphene.List)
|
||||
assert dynamic_field.type.of_type == A
|
||||
|
||||
|
||||
def test_should_onetoone_reverse_convert_model():
|
||||
|
@ -209,8 +215,10 @@ def test_should_onetoone_reverse_convert_model():
|
|||
model = FilmDetails
|
||||
|
||||
graphene_field = convert_django_field(related, A._meta.registry)
|
||||
assert isinstance(graphene_field, graphene.Field)
|
||||
assert graphene_field.type == get_graphql_type(A)
|
||||
assert isinstance(graphene_field, graphene.Dynamic)
|
||||
dynamic_field = graphene_field.get_type()
|
||||
assert isinstance(dynamic_field, graphene.Field)
|
||||
assert dynamic_field.type == A
|
||||
|
||||
|
||||
@pytest.mark.skipif(ArrayField is MissingType,
|
||||
|
@ -218,7 +226,7 @@ def test_should_onetoone_reverse_convert_model():
|
|||
def test_should_postgres_array_convert_list():
|
||||
field = assert_conversion(ArrayField, graphene.List, models.CharField(max_length=100))
|
||||
assert isinstance(field.type, graphene.List)
|
||||
assert field.type.of_type == get_graphql_type(graphene.String)
|
||||
assert field.type.of_type == graphene.String
|
||||
|
||||
|
||||
@pytest.mark.skipif(ArrayField is MissingType,
|
||||
|
@ -227,7 +235,7 @@ def test_should_postgres_array_multiple_convert_list():
|
|||
field = assert_conversion(ArrayField, graphene.List, ArrayField(models.CharField(max_length=100)))
|
||||
assert isinstance(field.type, graphene.List)
|
||||
assert isinstance(field.type.of_type, graphene.List)
|
||||
assert field.type.of_type.of_type == get_graphql_type(graphene.String)
|
||||
assert field.type.of_type.of_type == graphene.String
|
||||
|
||||
|
||||
@pytest.mark.skipif(HStoreField is MissingType,
|
||||
|
@ -248,4 +256,4 @@ def test_should_postgres_range_convert_list():
|
|||
from django.contrib.postgres.fields import IntegerRangeField
|
||||
field = assert_conversion(IntegerRangeField, graphene.List)
|
||||
assert isinstance(field.type, graphene.List)
|
||||
assert field.type.of_type == get_graphql_type(graphene.Int)
|
||||
assert field.type.of_type == graphene.Int
|
||||
|
|
|
@ -3,7 +3,7 @@ from py.test import raises
|
|||
|
||||
import graphene
|
||||
from ..form_converter import convert_form_field
|
||||
from graphene import ID, List
|
||||
from graphene import ID, List, NonNull
|
||||
from graphene.utils.get_graphql_type import get_graphql_type
|
||||
|
||||
from .models import Reporter
|
||||
|
@ -13,7 +13,7 @@ def assert_conversion(django_field, graphene_field, *args):
|
|||
field = django_field(*args, help_text='Custom Help Text')
|
||||
graphene_type = convert_form_field(field)
|
||||
assert isinstance(graphene_type, graphene_field)
|
||||
field = graphene_type.as_field()
|
||||
field = graphene_type.Field()
|
||||
assert field.description == 'Custom Help Text'
|
||||
return field
|
||||
|
||||
|
@ -75,12 +75,12 @@ def test_should_integer_convert_int():
|
|||
|
||||
def test_should_boolean_convert_boolean():
|
||||
field = assert_conversion(forms.BooleanField, graphene.Boolean)
|
||||
assert field.required is True
|
||||
assert isinstance(field.type, NonNull)
|
||||
|
||||
|
||||
def test_should_nullboolean_convert_boolean():
|
||||
field = assert_conversion(forms.NullBooleanField, graphene.Boolean)
|
||||
assert field.required is False
|
||||
assert not isinstance(field.type, NonNull)
|
||||
|
||||
|
||||
def test_should_float_convert_float():
|
||||
|
@ -95,7 +95,7 @@ def test_should_multiple_choice_convert_connectionorlist():
|
|||
field = forms.ModelMultipleChoiceField(Reporter.objects.all())
|
||||
graphene_type = convert_form_field(field)
|
||||
assert isinstance(graphene_type, List)
|
||||
assert graphene_type.of_type == get_graphql_type(ID)
|
||||
assert graphene_type.of_type == ID
|
||||
|
||||
|
||||
def test_should_manytoone_convert_connectionorlist():
|
||||
|
|
|
@ -121,10 +121,11 @@ def test_should_node():
|
|||
# reset_global_registry()
|
||||
# DjangoNode._meta.registry = get_global_registry()
|
||||
|
||||
class ReporterNode(DjangoNode, DjangoObjectType):
|
||||
class ReporterNode(DjangoObjectType):
|
||||
|
||||
class Meta:
|
||||
model = Reporter
|
||||
interfaces = (DjangoNode, )
|
||||
|
||||
@classmethod
|
||||
def get_node(cls, id, context, info):
|
||||
|
@ -133,10 +134,11 @@ def test_should_node():
|
|||
def resolve_articles(self, *args, **kwargs):
|
||||
return [Article(headline='Hi!')]
|
||||
|
||||
class ArticleNode(DjangoNode, DjangoObjectType):
|
||||
class ArticleNode(DjangoObjectType):
|
||||
|
||||
class Meta:
|
||||
model = Article
|
||||
interfaces = (DjangoNode, )
|
||||
|
||||
@classmethod
|
||||
def get_node(cls, id, context, info):
|
||||
|
|
|
@ -27,7 +27,7 @@ def test_should_map_fields_correctly():
|
|||
class Meta:
|
||||
model = Reporter
|
||||
registry = Registry()
|
||||
assert list(ReporterType2._meta.graphql_type.get_fields().keys()) == ['id', 'firstName', 'lastName', 'email', 'pets', 'aChoice']
|
||||
assert list(ReporterType2._meta.fields.keys()) == ['id', 'first_name', 'last_name', 'email', 'pets', 'a_choice', 'articles', 'films']
|
||||
|
||||
|
||||
def test_should_map_only_few_fields():
|
||||
|
@ -35,6 +35,6 @@ def test_should_map_only_few_fields():
|
|||
|
||||
class Meta:
|
||||
model = Reporter
|
||||
fields = ('id', 'email')
|
||||
only_fields = ('id', 'email')
|
||||
|
||||
assert list(Reporter2._meta.graphql_type.get_fields().keys()) == ['id', 'email']
|
||||
assert list(Reporter2._meta.fields.keys()) == ['id', 'email']
|
||||
|
|
|
@ -6,21 +6,22 @@ from graphene.relay import Node, ConnectionField
|
|||
from ..types import DjangoNode, DjangoObjectType
|
||||
|
||||
from .models import Article as ArticleModel, Reporter as ReporterModel
|
||||
from ..registry import reset_global_registry, Registry
|
||||
|
||||
reset_global_registry()
|
||||
|
||||
|
||||
class Reporter(DjangoObjectType):
|
||||
'''Character description'''
|
||||
'''Reporter description'''
|
||||
class Meta:
|
||||
model = ReporterModel
|
||||
|
||||
|
||||
class Article(DjangoNode, DjangoObjectType):
|
||||
'''Human description'''
|
||||
|
||||
pub_date = Int()
|
||||
|
||||
class Article(DjangoObjectType):
|
||||
'''Article description'''
|
||||
class Meta:
|
||||
model = ArticleModel
|
||||
interfaces = (DjangoNode, )
|
||||
|
||||
|
||||
class RootQuery(ObjectType):
|
||||
|
@ -43,13 +44,13 @@ def test_django_get_node(get):
|
|||
|
||||
|
||||
def test_django_objecttype_map_correct_fields():
|
||||
graphql_type = Reporter._meta.graphql_type
|
||||
assert list(graphql_type.get_fields().keys()) == ['id', 'firstName', 'lastName', 'email', 'pets', 'aChoice', 'articles']
|
||||
fields = Reporter._meta.fields
|
||||
assert list(fields.keys()) == ['id', 'first_name', 'last_name', 'email', 'pets', 'a_choice', 'articles', 'films']
|
||||
|
||||
|
||||
def test_django_objecttype_with_node_have_correct_fields():
|
||||
graphql_type = Article._meta.graphql_type
|
||||
assert list(graphql_type.get_fields().keys()) == ['id', 'pubDate', 'headline', 'reporter', 'lang', 'importance']
|
||||
fields = Article._meta.fields
|
||||
assert list(fields.keys()) == ['id', 'headline', 'pub_date', 'reporter', 'lang', 'importance']
|
||||
|
||||
|
||||
def test_schema_representation():
|
||||
|
@ -60,8 +61,8 @@ schema {
|
|||
|
||||
type Article implements Node {
|
||||
id: ID!
|
||||
pubDate: Int
|
||||
headline: String
|
||||
pubDate: DateTime
|
||||
reporter: Reporter
|
||||
lang: ArticleLang
|
||||
importance: ArticleImportance
|
||||
|
@ -87,6 +88,8 @@ enum ArticleLang {
|
|||
ENGLISH
|
||||
}
|
||||
|
||||
scalar DateTime
|
||||
|
||||
interface Node {
|
||||
id: ID!
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
from collections import OrderedDict
|
||||
from functools import partial
|
||||
|
||||
import six
|
||||
|
||||
from graphene import Field, Interface
|
||||
from graphene.types.objecttype import ObjectType, ObjectTypeMeta, attrs_without_fields, get_interfaces
|
||||
from graphene import Field, Interface, ObjectType
|
||||
from graphene.types.objecttype import ObjectTypeMeta
|
||||
from graphene.relay import Node
|
||||
from graphene.relay.node import NodeMeta
|
||||
from .converter import convert_django_field_with_choices
|
||||
|
@ -11,22 +12,18 @@ from graphene.types.options import Options
|
|||
from .utils import get_model_fields, is_valid_django_model, DJANGO_FILTER_INSTALLED
|
||||
from .registry import Registry, get_global_registry
|
||||
from graphene.utils.is_base_type import is_base_type
|
||||
from graphene.utils.copy_fields import copy_fields
|
||||
from graphene.utils.get_graphql_type import get_graphql_type
|
||||
from graphene.utils.get_fields import get_fields
|
||||
from graphene.utils.as_field import as_field
|
||||
from graphene.generators import generate_objecttype
|
||||
from graphene.types.utils import get_fields_in_type
|
||||
|
||||
|
||||
class DjangoObjectTypeMeta(ObjectTypeMeta):
|
||||
def _construct_fields(cls, fields, options):
|
||||
def _construct_fields(cls, all_fields, options):
|
||||
_model_fields = get_model_fields(options.model)
|
||||
|
||||
fields = OrderedDict()
|
||||
for field in _model_fields:
|
||||
name = field.name
|
||||
is_not_in_only = options.fields and name not in options.fields
|
||||
is_already_created = name in fields
|
||||
is_excluded = field.name in options.exclude or is_already_created
|
||||
is_not_in_only = options.only_fields and name not in options.only_fields
|
||||
is_already_created = name in all_fields
|
||||
is_excluded = name in options.exclude_fields or is_already_created
|
||||
if is_not_in_only or is_excluded:
|
||||
# We skip this field if we specify only_fields and is not
|
||||
# in there. Or when we exclude this field in exclude_fields
|
||||
|
@ -34,28 +31,24 @@ class DjangoObjectTypeMeta(ObjectTypeMeta):
|
|||
converted = convert_django_field_with_choices(field, options.registry)
|
||||
if not converted:
|
||||
continue
|
||||
fields[name] = as_field(converted)
|
||||
|
||||
fields = copy_fields(Field, fields, parent=cls)
|
||||
fields[name] = converted
|
||||
|
||||
return fields
|
||||
|
||||
@staticmethod
|
||||
def _create_objecttype(cls, name, bases, attrs):
|
||||
# super_new = super(DjangoObjectTypeMeta, cls).__new__
|
||||
super_new = type.__new__
|
||||
|
||||
# Also ensure initialization is only performed for subclasses of Model
|
||||
# (excluding Model class itself).
|
||||
def __new__(cls, name, bases, attrs):
|
||||
# Also ensure initialization is only performed for subclasses of
|
||||
# DjangoObjectType
|
||||
if not is_base_type(bases, DjangoObjectTypeMeta):
|
||||
return super_new(cls, name, bases, attrs)
|
||||
return type.__new__(cls, name, bases, attrs)
|
||||
|
||||
defaults = dict(
|
||||
name=None,
|
||||
description=None,
|
||||
name=name,
|
||||
description=attrs.pop('__doc__', None),
|
||||
model=None,
|
||||
fields=(),
|
||||
exclude=(),
|
||||
fields=None,
|
||||
only_fields=(),
|
||||
exclude_fields=(),
|
||||
interfaces=(),
|
||||
registry=None
|
||||
)
|
||||
|
@ -73,52 +66,40 @@ class DjangoObjectTypeMeta(ObjectTypeMeta):
|
|||
)
|
||||
if not options.registry:
|
||||
options.registry = get_global_registry()
|
||||
assert isinstance(options.registry, Registry), 'The attribute registry in {}.Meta needs to be an instance of Registry, received "{}".'.format(name, options.registry)
|
||||
assert is_valid_django_model(options.model), 'You need to pass a valid Django Model in {}.Meta, received "{}".'.format(name, options.model)
|
||||
assert isinstance(options.registry, Registry), (
|
||||
'The attribute registry in {}.Meta needs to be an instance of '
|
||||
'Registry, received "{}".'
|
||||
).format(name, options.registry)
|
||||
assert is_valid_django_model(options.model), (
|
||||
'You need to pass a valid Django Model in {}.Meta, received "{}".'
|
||||
).format(name, options.model)
|
||||
|
||||
interfaces = tuple(options.interfaces)
|
||||
fields = get_fields(ObjectType, attrs, bases, interfaces)
|
||||
attrs = attrs_without_fields(attrs, fields)
|
||||
cls = super_new(cls, name, bases, dict(attrs, _meta=options))
|
||||
# interfaces = tuple(options.interfaces)
|
||||
# fields = get_fields(ObjectType, attrs, bases, interfaces)
|
||||
# attrs = attrs_without_fields(attrs, fields)
|
||||
cls = ObjectTypeMeta.__new__(cls, name, bases, dict(attrs, _meta=options))
|
||||
|
||||
base_interfaces = tuple(b for b in bases if issubclass(b, Interface))
|
||||
options.get_fields = partial(cls._construct_fields, fields, options)
|
||||
options.get_interfaces = tuple(get_interfaces(interfaces + base_interfaces))
|
||||
options.registry.register(cls)
|
||||
|
||||
options.graphql_type = generate_objecttype(cls)
|
||||
|
||||
if issubclass(cls, DjangoObjectType):
|
||||
options.registry.register(cls)
|
||||
options.django_fields = get_fields_in_type(
|
||||
ObjectType,
|
||||
cls._construct_fields(options.fields, options)
|
||||
)
|
||||
options.fields.update(options.django_fields)
|
||||
|
||||
return cls
|
||||
|
||||
|
||||
class DjangoObjectType(six.with_metaclass(DjangoObjectTypeMeta, ObjectType)):
|
||||
is_type_of = None
|
||||
@classmethod
|
||||
def is_type_of(cls, root, context, info):
|
||||
if isinstance(root, cls):
|
||||
return True
|
||||
if not is_valid_django_model(type(root)):
|
||||
raise Exception('Received incompatible instance "{}"'.format(root))
|
||||
model = root._meta.model
|
||||
return model == cls._meta.model
|
||||
|
||||
|
||||
class DjangoNodeMeta(DjangoObjectTypeMeta, NodeMeta):
|
||||
|
||||
@staticmethod
|
||||
def _get_interface_options(meta):
|
||||
return Options(
|
||||
meta,
|
||||
name=None,
|
||||
description=None,
|
||||
graphql_type=None,
|
||||
registry=False
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _create_interface(cls, name, bases, attrs):
|
||||
cls = super(DjangoNodeMeta, cls)._create_interface(cls, name, bases, attrs)
|
||||
if not cls._meta.registry:
|
||||
cls._meta.registry = get_global_registry()
|
||||
assert isinstance(cls._meta.registry, Registry), 'The attribute registry in {}.Meta needs to be an instance of Registry.'.format(name)
|
||||
return cls
|
||||
|
||||
|
||||
class DjangoNode(six.with_metaclass(DjangoNodeMeta, Node)):
|
||||
@classmethod
|
||||
def get_node(cls, id, context, info):
|
||||
try:
|
||||
|
@ -126,12 +107,23 @@ class DjangoNode(six.with_metaclass(DjangoNodeMeta, Node)):
|
|||
except cls._meta.model.DoesNotExist:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def resolve_type(cls, type, context, info):
|
||||
# We get the model from the _meta in the Django class/instance
|
||||
model = type._meta.model
|
||||
graphene_type = cls._meta.registry.get_type_for_model(model)
|
||||
if graphene_type:
|
||||
return get_graphql_type(graphene_type)
|
||||
DjangoNode = Node
|
||||
|
||||
raise Exception("Type not found for model \"{}\"".format(model))
|
||||
# class DjangoNodeMeta(DjangoObjectTypeMeta, NodeMeta):
|
||||
# pass
|
||||
|
||||
|
||||
# class DjangoNode(six.with_metaclass(DjangoNodeMeta, Node)):
|
||||
# @classmethod
|
||||
# def get_node(cls, id, context, info):
|
||||
# try:
|
||||
# return cls._meta.model.objects.get(id=id)
|
||||
# except cls._meta.model.DoesNotExist:
|
||||
# return None
|
||||
|
||||
# @classmethod
|
||||
# def resolve_type(cls, type, context, info):
|
||||
# # We get the model from the _meta in the Django class/instance
|
||||
# model = type._meta.model
|
||||
# graphene_type = cls._meta.registry.get_type_for_model(model)
|
||||
# return graphene_type
|
||||
|
|
Loading…
Reference in New Issue
Block a user