mirror of
https://github.com/graphql-python/graphene.git
synced 2025-02-02 12:44:15 +03:00
Updated first passing Django tests! 🎉
This commit is contained in:
parent
b772499b9b
commit
7a29502790
|
@ -49,9 +49,9 @@ class IntroduceShip(relay.ClientIDMutation):
|
||||||
faction = graphene.Field(Faction)
|
faction = graphene.Field(Faction)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def mutate_and_get_payload(cls, input, info):
|
def mutate_and_get_payload(cls, input, context, info):
|
||||||
ship_name = input.get('ship_name')
|
ship_name = input.get('shipName')
|
||||||
faction_id = input.get('faction_id')
|
faction_id = input.get('factionId')
|
||||||
ship = create_ship(ship_name, faction_id)
|
ship = create_ship(ship_name, faction_id)
|
||||||
faction = get_faction(faction_id)
|
faction = get_faction(faction_id)
|
||||||
return IntroduceShip(ship=ship, faction=faction)
|
return IntroduceShip(ship=ship, faction=faction)
|
||||||
|
@ -60,7 +60,7 @@ class IntroduceShip(relay.ClientIDMutation):
|
||||||
class Query(graphene.ObjectType):
|
class Query(graphene.ObjectType):
|
||||||
rebels = graphene.Field(Faction)
|
rebels = graphene.Field(Faction)
|
||||||
empire = graphene.Field(Faction)
|
empire = graphene.Field(Faction)
|
||||||
node = relay.Node.Field()
|
node = DjangoNode.Field()
|
||||||
ships = relay.ConnectionField(Ship, description='All the ships.')
|
ships = relay.ConnectionField(Ship, description='All the ships.')
|
||||||
|
|
||||||
@resolve_only_args
|
@resolve_only_args
|
||||||
|
@ -77,9 +77,9 @@ class Query(graphene.ObjectType):
|
||||||
|
|
||||||
|
|
||||||
class Mutation(graphene.ObjectType):
|
class Mutation(graphene.ObjectType):
|
||||||
introduce_ship = graphene.Field(IntroduceShip)
|
introduce_ship = IntroduceShip.Field()
|
||||||
|
|
||||||
|
|
||||||
# We register the Character Model because if not would be
|
# We register the Character Model because if not would be
|
||||||
# inaccessible for the schema
|
# inaccessible for the schema
|
||||||
schema = Schema(query=Query, mutation=Mutation, types=[])
|
schema = Schema(query=Query, mutation=Mutation, types=[Ship, Character])
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
# from ...core.exceptions import SkipField
|
from django.db.models.query import QuerySet
|
||||||
from graphene import Field, List
|
|
||||||
from graphene.relay import ConnectionField
|
from graphene.relay import ConnectionField
|
||||||
from .utils import DJANGO_FILTER_INSTALLED, get_type_for_model, maybe_queryset
|
from graphql_relay.connection.arrayconnection import connection_from_list_slice
|
||||||
|
from .utils import maybe_queryset
|
||||||
|
|
||||||
|
|
||||||
class DjangoConnectionField(ConnectionField):
|
class DjangoConnectionField(ConnectionField):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.on = kwargs.pop('on', False)
|
self.on = kwargs.pop('on', False)
|
||||||
# kwargs['default'] = kwargs.pop('default', self.get_manager)
|
|
||||||
return super(DjangoConnectionField, self).__init__(*args, **kwargs)
|
return super(DjangoConnectionField, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def model(self):
|
def model(self):
|
||||||
return self.type._meta.model
|
return self.connection._meta.node._meta.model
|
||||||
|
|
||||||
def get_manager(self):
|
def get_manager(self):
|
||||||
if self.on:
|
if self.on:
|
||||||
|
@ -21,44 +20,22 @@ class DjangoConnectionField(ConnectionField):
|
||||||
else:
|
else:
|
||||||
return self.model._default_manager
|
return self.model._default_manager
|
||||||
|
|
||||||
def get_queryset(self, resolved_qs, args, info):
|
def default_resolver(self, root, args, context, info):
|
||||||
return resolved_qs
|
return getattr(root, self.source or self.attname, self.get_manager())
|
||||||
|
|
||||||
def from_list(self, connection_type, resolved, args, context, info):
|
def connection_resolver(self, root, args, context, info):
|
||||||
resolved_qs = maybe_queryset(resolved)
|
iterable = super(ConnectionField, self).resolver(root, args, context, info)
|
||||||
qs = self.get_queryset(resolved_qs, args, info)
|
iterable = maybe_queryset(iterable)
|
||||||
return super(DjangoConnectionField, self).from_list(connection_type, qs, args, context, info)
|
if isinstance(iterable, QuerySet):
|
||||||
|
_len = iterable.count()
|
||||||
|
else:
|
||||||
def get_list_or_connection_type_for_model(model):
|
_len = len(iterable)
|
||||||
pass
|
return connection_from_list_slice(
|
||||||
# field_object_type = model_field.get_object_type(schema)
|
iterable,
|
||||||
# if not field_object_type:
|
args,
|
||||||
# raise SkipField()
|
slice_start=0,
|
||||||
# if isinstance(:
|
list_length=_len,
|
||||||
# if field_object_type._meta.filter_fields:
|
list_slice_length=_len,
|
||||||
# field = DjangoFilterConnectionField(field_object_type)
|
connection_type=self.connection,
|
||||||
# else:
|
edge_type=self.connection.Edge,
|
||||||
# field = DjangoConnectionField(field_object_type)
|
)
|
||||||
# else:
|
|
||||||
# field = List(field_object_type)
|
|
||||||
# field.contribute_to_class(self.object_type, self.attname)
|
|
||||||
# return schema.T(field)
|
|
||||||
|
|
||||||
|
|
||||||
def get_graphene_type_from_model(model):
|
|
||||||
pass
|
|
||||||
# _type = self.get_object_type(schema)
|
|
||||||
# if not _type and self.parent._meta.only_fields:
|
|
||||||
# raise Exception(
|
|
||||||
# "Model %r is not accessible by the schema. "
|
|
||||||
# "You can either register the type manually "
|
|
||||||
# "using @schema.register. "
|
|
||||||
# "Or disable the field in %s" % (
|
|
||||||
# self.model,
|
|
||||||
# self.parent,
|
|
||||||
# )
|
|
||||||
# )
|
|
||||||
# if not _type:
|
|
||||||
# raise SkipField()
|
|
||||||
# return schema.T(_type)
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ from six import StringIO
|
||||||
|
|
||||||
@patch('graphene_django.management.commands.graphql_schema.Command.save_file')
|
@patch('graphene_django.management.commands.graphql_schema.Command.save_file')
|
||||||
def test_generate_file_on_call_graphql_schema(savefile_mock, settings):
|
def test_generate_file_on_call_graphql_schema(savefile_mock, settings):
|
||||||
settings.GRAPHENE_SCHEMA = 'graphene_django.tests.test_urls'
|
settings.GRAPHENE_SCHEMA = 'graphene_django.tests.urls'
|
||||||
out = StringIO()
|
out = StringIO()
|
||||||
management.call_command('graphql_schema', schema='', stdout=out)
|
management.call_command('graphql_schema', schema='', stdout=out)
|
||||||
assert "Successfully dumped GraphQL schema to schema.json" in out.getvalue()
|
assert "Successfully dumped GraphQL schema to schema.json" in out.getvalue()
|
||||||
|
|
|
@ -115,9 +115,8 @@ def test_field_with_choices_convert_enum():
|
||||||
app_label = 'test'
|
app_label = 'test'
|
||||||
|
|
||||||
graphene_type = convert_django_field_with_choices(field)
|
graphene_type = convert_django_field_with_choices(field)
|
||||||
assert issubclass(graphene_type, graphene.Enum)
|
assert isinstance(graphene_type, graphene.Enum)
|
||||||
assert graphene_type._meta.graphql_type.name == 'TEST_TRANSLATEDMODEL_LANGUAGE'
|
assert graphene_type._meta.graphql_type.name == 'TranslatedModelLanguage'
|
||||||
assert graphene_type._meta.graphql_type.description == 'Language'
|
|
||||||
assert graphene_type._meta.enum.__members__['SPANISH'].value == 'es'
|
assert graphene_type._meta.enum.__members__['SPANISH'].value == 'es'
|
||||||
assert graphene_type._meta.enum.__members__['ENGLISH'].value == 'en'
|
assert graphene_type._meta.enum.__members__['ENGLISH'].value == 'en'
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,10 @@ from django.db import models
|
||||||
from py.test import raises
|
from py.test import raises
|
||||||
|
|
||||||
import graphene
|
import graphene
|
||||||
from graphene import relay
|
|
||||||
|
|
||||||
from ..compat import MissingType, RangeField
|
from ..compat import MissingType, RangeField
|
||||||
from ..types import DjangoNode, DjangoObjectType
|
from ..types import DjangoNode, DjangoObjectType
|
||||||
from ..registry import reset_global_registry
|
from ..registry import reset_global_registry, get_global_registry
|
||||||
from .models import Article, Reporter
|
from .models import Article, Reporter
|
||||||
|
|
||||||
pytestmark = pytest.mark.django_db
|
pytestmark = pytest.mark.django_db
|
||||||
|
@ -43,7 +42,7 @@ def test_should_query_well():
|
||||||
reporter = graphene.Field(ReporterType)
|
reporter = graphene.Field(ReporterType)
|
||||||
|
|
||||||
def resolve_reporter(self, *args, **kwargs):
|
def resolve_reporter(self, *args, **kwargs):
|
||||||
return ReporterType(Reporter(first_name='ABA', last_name='X'))
|
return Reporter(first_name='ABA', last_name='X')
|
||||||
|
|
||||||
query = '''
|
query = '''
|
||||||
query ReporterQuery {
|
query ReporterQuery {
|
||||||
|
@ -119,37 +118,37 @@ def test_should_query_postgres_fields():
|
||||||
|
|
||||||
|
|
||||||
def test_should_node():
|
def test_should_node():
|
||||||
reset_global_registry()
|
# reset_global_registry()
|
||||||
|
# DjangoNode._meta.registry = get_global_registry()
|
||||||
|
|
||||||
class ReporterNode(DjangoNode):
|
class ReporterNode(DjangoNode, DjangoObjectType):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Reporter
|
model = Reporter
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_node(cls, id, info):
|
def get_node(cls, id, context, info):
|
||||||
return ReporterNode(Reporter(id=2, first_name='Cookie Monster'))
|
return Reporter(id=2, first_name='Cookie Monster')
|
||||||
|
|
||||||
def resolve_articles(self, *args, **kwargs):
|
def resolve_articles(self, *args, **kwargs):
|
||||||
return [Article(headline='Hi!')]
|
return [Article(headline='Hi!')]
|
||||||
|
|
||||||
class ArticleNode(DjangoNode):
|
class ArticleNode(DjangoNode, DjangoObjectType):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Article
|
model = Article
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_node(cls, id, info):
|
def get_node(cls, id, context, info):
|
||||||
return Article(id=1, headline='Article node', pub_date=datetime.date(2002, 3, 11))
|
return Article(id=1, headline='Article node', pub_date=datetime.date(2002, 3, 11))
|
||||||
|
|
||||||
class Query(graphene.ObjectType):
|
class Query(graphene.ObjectType):
|
||||||
node = relay.Node.Field()
|
node = DjangoNode.Field()
|
||||||
reporter = graphene.Field(ReporterNode)
|
reporter = graphene.Field(ReporterNode)
|
||||||
article = graphene.Field(ArticleNode)
|
article = graphene.Field(ArticleNode)
|
||||||
|
|
||||||
def resolve_reporter(self, *args, **kwargs):
|
def resolve_reporter(self, *args, **kwargs):
|
||||||
return ReporterNode(
|
return Reporter(id=1, first_name='ABA', last_name='X')
|
||||||
Reporter(id=1, first_name='ABA', last_name='X'))
|
|
||||||
|
|
||||||
query = '''
|
query = '''
|
||||||
query ReporterQuery {
|
query ReporterQuery {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from py.test import raises
|
from py.test import raises
|
||||||
|
|
||||||
from ..types import DjangoObjectType
|
from ..types import DjangoObjectType
|
||||||
from tests.utils import assert_equal_lists
|
from ..registry import Registry
|
||||||
|
|
||||||
from .models import Reporter
|
from .models import Reporter
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ def test_should_raise_if_no_model():
|
||||||
with raises(Exception) as excinfo:
|
with raises(Exception) as excinfo:
|
||||||
class Character1(DjangoObjectType):
|
class Character1(DjangoObjectType):
|
||||||
pass
|
pass
|
||||||
assert 'model in the Meta' in str(excinfo.value)
|
assert 'valid Django Model' in str(excinfo.value)
|
||||||
|
|
||||||
|
|
||||||
def test_should_raise_if_model_is_invalid():
|
def test_should_raise_if_model_is_invalid():
|
||||||
|
@ -19,18 +19,15 @@ def test_should_raise_if_model_is_invalid():
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = 1
|
model = 1
|
||||||
assert 'not a Django model' in str(excinfo.value)
|
assert 'valid Django Model' in str(excinfo.value)
|
||||||
|
|
||||||
|
|
||||||
def test_should_map_fields_correctly():
|
def test_should_map_fields_correctly():
|
||||||
class ReporterType2(DjangoObjectType):
|
class ReporterType2(DjangoObjectType):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Reporter
|
model = Reporter
|
||||||
assert_equal_lists(
|
registry = Registry()
|
||||||
ReporterType2._meta.fields_map.keys(),
|
assert list(ReporterType2._meta.graphql_type.get_fields().keys()) == ['id', 'firstName', 'lastName', 'email', 'pets', 'aChoice']
|
||||||
['articles', 'first_name', 'last_name', 'email', 'pets', 'id', 'films']
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_should_map_only_few_fields():
|
def test_should_map_only_few_fields():
|
||||||
|
@ -38,8 +35,6 @@ def test_should_map_only_few_fields():
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Reporter
|
model = Reporter
|
||||||
only_fields = ('id', 'email')
|
fields = ('id', 'email')
|
||||||
assert_equal_lists(
|
|
||||||
Reporter2._meta.fields_map.keys(),
|
assert list(Reporter2._meta.graphql_type.get_fields().keys()) == ['id', 'email']
|
||||||
['id', 'email']
|
|
||||||
)
|
|
||||||
|
|
|
@ -2,13 +2,13 @@ from django.conf.urls import url
|
||||||
|
|
||||||
import graphene
|
import graphene
|
||||||
from graphene import Schema
|
from graphene import Schema
|
||||||
from ..types import DjangoNode
|
from ..types import DjangoNode, DjangoObjectType
|
||||||
from ..views import GraphQLView
|
from ..views import GraphQLView
|
||||||
|
|
||||||
from .models import Article, Reporter
|
from .models import Article, Reporter
|
||||||
|
|
||||||
|
|
||||||
class Character(DjangoNode):
|
class Character(DjangoNode, DjangoObjectType):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Reporter
|
model = Reporter
|
||||||
|
@ -17,7 +17,7 @@ class Character(DjangoNode):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Human(DjangoNode):
|
class Human(DjangoNode, DjangoObjectType):
|
||||||
raises = graphene.String()
|
raises = graphene.String()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -1,21 +1,18 @@
|
||||||
import inspect
|
|
||||||
from collections import OrderedDict
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
import six
|
import six
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
from graphene import Field, Interface
|
from graphene import Field, Interface
|
||||||
from graphene.types.objecttype import ObjectType, ObjectTypeMeta, attrs_without_fields, GrapheneObjectType, get_interfaces
|
from graphene.types.objecttype import ObjectType, ObjectTypeMeta, attrs_without_fields, GrapheneObjectType, get_interfaces
|
||||||
from graphene.types.interface import InterfaceTypeMeta
|
from graphene.relay import Node
|
||||||
from graphene.relay import Connection, Node
|
|
||||||
from graphene.relay.node import NodeMeta
|
from graphene.relay.node import NodeMeta
|
||||||
from .converter import convert_django_field_with_choices
|
from .converter import convert_django_field_with_choices
|
||||||
from graphene.types.options import Options
|
from graphene.types.options import Options
|
||||||
from .utils import get_model_fields
|
from .utils import get_model_fields, is_valid_django_model
|
||||||
from .registry import Registry, get_global_registry
|
from .registry import Registry, get_global_registry
|
||||||
from graphene.utils.is_base_type import is_base_type
|
from graphene.utils.is_base_type import is_base_type
|
||||||
from graphene.utils.copy_fields import copy_fields
|
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.get_fields import get_fields
|
||||||
from graphene.utils.as_field import as_field
|
from graphene.utils.as_field import as_field
|
||||||
|
|
||||||
|
@ -63,8 +60,8 @@ class DjangoObjectTypeMeta(ObjectTypeMeta):
|
||||||
)
|
)
|
||||||
if not options.registry:
|
if not options.registry:
|
||||||
options.registry = get_global_registry()
|
options.registry = get_global_registry()
|
||||||
assert isinstance(options.registry, Registry), 'The attribute registry in {}.Meta needs to be an instance of Registry.'.format(name)
|
assert isinstance(options.registry, Registry), 'The attribute registry in {}.Meta needs to be an instance of Registry, received "{}".'.format(name, options.registry)
|
||||||
assert options.model, 'You need to pass a valid Django Model in {}.Meta'.format(name)
|
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)
|
interfaces = tuple(options.interfaces)
|
||||||
fields = get_fields(ObjectType, attrs, bases, interfaces)
|
fields = get_fields(ObjectType, attrs, bases, interfaces)
|
||||||
|
@ -80,7 +77,7 @@ class DjangoObjectTypeMeta(ObjectTypeMeta):
|
||||||
interfaces=tuple(get_interfaces(interfaces + base_interfaces))
|
interfaces=tuple(get_interfaces(interfaces + base_interfaces))
|
||||||
)
|
)
|
||||||
|
|
||||||
if issubclass(cls, DjangoObjectType):
|
if issubclass(cls, DjangoObjectType):
|
||||||
options.registry.register(cls)
|
options.registry.register(cls)
|
||||||
|
|
||||||
return cls
|
return cls
|
||||||
|
@ -90,8 +87,24 @@ class DjangoObjectType(six.with_metaclass(DjangoObjectTypeMeta, ObjectType)):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class DjangoNodeMeta(DjangoObjectTypeMeta, NodeMeta):
|
class DjangoNodeMeta(NodeMeta, DjangoObjectTypeMeta):
|
||||||
pass
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_interface_options(meta):
|
||||||
|
return Options(
|
||||||
|
meta,
|
||||||
|
name=None,
|
||||||
|
description=None,
|
||||||
|
graphql_type=None,
|
||||||
|
registry=False
|
||||||
|
)
|
||||||
|
|
||||||
|
def __new__(cls, name, bases, attrs):
|
||||||
|
cls = super(DjangoNodeMeta, cls).__new__(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)):
|
class DjangoNode(six.with_metaclass(DjangoNodeMeta, Node)):
|
||||||
|
@ -101,3 +114,13 @@ class DjangoNode(six.with_metaclass(DjangoNodeMeta, Node)):
|
||||||
return cls._meta.model.objects.get(id=id)
|
return cls._meta.model.objects.get(id=id)
|
||||||
except cls._meta.model.DoesNotExist:
|
except cls._meta.model.DoesNotExist:
|
||||||
return None
|
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)
|
||||||
|
|
||||||
|
raise Exception("Type not found for model \"{}\"".format(model))
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import inspect
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.manager import Manager
|
from django.db.models.manager import Manager
|
||||||
from django.db.models.query import QuerySet
|
|
||||||
|
|
||||||
# from graphene.utils import LazyList
|
# from graphene.utils import LazyList
|
||||||
class LazyList(object):
|
class LazyList(object):
|
||||||
|
@ -54,8 +54,6 @@ class WrappedQueryset(LazyList):
|
||||||
def maybe_queryset(value):
|
def maybe_queryset(value):
|
||||||
if isinstance(value, Manager):
|
if isinstance(value, Manager):
|
||||||
value = value.get_queryset()
|
value = value.get_queryset()
|
||||||
if isinstance(value, QuerySet):
|
|
||||||
return WrappedQueryset(value)
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
@ -75,6 +73,10 @@ def get_related_model(field):
|
||||||
return field.related_model
|
return field.related_model
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_django_model(model):
|
||||||
|
return inspect.isclass(model) and issubclass(model, models.Model)
|
||||||
|
|
||||||
|
|
||||||
def import_single_dispatch():
|
def import_single_dispatch():
|
||||||
try:
|
try:
|
||||||
from functools import singledispatch
|
from functools import singledispatch
|
||||||
|
|
|
@ -2,11 +2,8 @@ from graphql_django_view import GraphQLView as BaseGraphQLView
|
||||||
|
|
||||||
|
|
||||||
class GraphQLView(BaseGraphQLView):
|
class GraphQLView(BaseGraphQLView):
|
||||||
graphene_schema = None
|
|
||||||
|
|
||||||
def __init__(self, schema, **kwargs):
|
def __init__(self, schema, **kwargs):
|
||||||
super(GraphQLView, self).__init__(
|
super(GraphQLView, self).__init__(
|
||||||
graphene_schema=schema,
|
|
||||||
schema=schema,
|
schema=schema,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user