Updated first passing Django tests! 🎉

This commit is contained in:
Syrus Akbary 2016-06-19 14:30:33 -07:00
parent b772499b9b
commit 7a29502790
10 changed files with 92 additions and 100 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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