Skip to content

Commit

Permalink
Merge branch '1.0-django-interfaceobjecttype' into external-resolvers
Browse files Browse the repository at this point in the history
  • Loading branch information
syrusakbary committed Jun 21, 2016
2 parents 1ac1cc9 + b545849 commit fcba194
Show file tree
Hide file tree
Showing 99 changed files with 2,970 additions and 241 deletions.
9 changes: 0 additions & 9 deletions examples/starwars/tests/test_schema.py
Original file line number Diff line number Diff line change
@@ -1,9 +0,0 @@

from ..schema import Droid


def test_query_types():
graphql_type = Droid._meta.graphql_type
fields = graphql_type.get_fields()
assert fields['friends'].parent == Droid
assert fields
14 changes: 14 additions & 0 deletions examples/starwars_relay/tests/test_connections.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ def test_correct_fetch_first_ship_rebels():
rebels {
name,
ships(first: 1) {
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
edges {
cursor
node {
name
}
Expand All @@ -23,8 +30,15 @@ def test_correct_fetch_first_ship_rebels():
'rebels': {
'name': 'Alliance to Restore the Republic',
'ships': {
'pageInfo': {
'startCursor': 'YXJyYXljb25uZWN0aW9uOjA=',
'endCursor': 'YXJyYXljb25uZWN0aW9uOjA=',
'hasNextPage': True,
'hasPreviousPage': False
},
'edges': [
{
'cursor': 'YXJyYXljb25uZWN0aW9uOjA=',
'node': {
'name': 'X-Wing'
}
Expand Down
18 changes: 18 additions & 0 deletions graphene-django/django_test_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import sys, os
ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, ROOT_PATH + '/examples/')

SECRET_KEY = 1

INSTALLED_APPS = [
'graphene_django',
'graphene_django.tests',
'starwars',
]

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'django_test.sqlite',
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
import graphene
from graphene import relay, resolve_only_args
from graphene.contrib.django import DjangoNode, DjangoObjectType
from graphene import relay, resolve_only_args, Schema
from graphene_django import DjangoNode, DjangoObjectType

from .data import (create_ship, get_empire, get_faction, get_rebels, get_ship,
get_ships)
from .models import Character as CharacterModel
from .models import Faction as FactionModel
from .models import Ship as ShipModel
from .models import (
Character as CharacterModel,
Faction as FactionModel,
Ship as ShipModel
)

schema = graphene.Schema(name='Starwars Django Relay Schema')


class Ship(DjangoNode):
class Ship(DjangoNode, DjangoObjectType):

class Meta:
model = ShipModel

@classmethod
def get_node(cls, id, info):
return Ship(get_ship(id))
def get_node(cls, id, context, info):
node = get_ship(id)
print(node)
return node


class Character(DjangoObjectType):
Expand All @@ -27,14 +29,14 @@ class Meta:
model = CharacterModel


class Faction(DjangoNode):
class Faction(DjangoNode, DjangoObjectType):

class Meta:
model = FactionModel

@classmethod
def get_node(cls, id, info):
return Faction(get_faction(id))
def get_node(cls, id, context, info):
return get_faction(id)


class IntroduceShip(relay.ClientIDMutation):
Expand All @@ -47,18 +49,18 @@ class Input:
faction = graphene.Field(Faction)

@classmethod
def mutate_and_get_payload(cls, input, info):
ship_name = input.get('ship_name')
faction_id = input.get('faction_id')
def mutate_and_get_payload(cls, input, context, info):
ship_name = input.get('shipName')
faction_id = input.get('factionId')
ship = create_ship(ship_name, faction_id)
faction = get_faction(faction_id)
return IntroduceShip(ship=Ship(ship), faction=Faction(faction))
return IntroduceShip(ship=ship, faction=faction)


class Query(graphene.ObjectType):
rebels = graphene.Field(Faction)
empire = graphene.Field(Faction)
node = relay.NodeField()
node = DjangoNode.Field()
ships = relay.ConnectionField(Ship, description='All the ships.')

@resolve_only_args
Expand All @@ -75,12 +77,9 @@ def resolve_empire(self):


class Mutation(graphene.ObjectType):
introduce_ship = graphene.Field(IntroduceShip)
introduce_ship = IntroduceShip.Field()


# We register the Character Model because if not would be
# inaccessible for the schema
schema.register(Character)

schema.query = Query
schema.mutation = Mutation
schema = Schema(query=Query, mutation=Mutation, types=[Ship, Character])
10 changes: 10 additions & 0 deletions graphene-django/graphene_django/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from .types import (
DjangoObjectType,
DjangoNode
)
from .fields import (
DjangoConnectionField,
)

__all__ = ['DjangoObjectType', 'DjangoNode',
'DjangoConnectionField']
24 changes: 24 additions & 0 deletions graphene-django/graphene_django/compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from django.db import models


class MissingType(object):
pass

try:
UUIDField = models.UUIDField
except AttributeError:
# Improved compatibility for Django 1.6
UUIDField = MissingType

try:
from django.db.models.related import RelatedObject
except:
# Improved compatibility for Django 1.6
RelatedObject = MissingType


try:
# Postgres fields are only available in Django 1.8+
from django.contrib.postgres.fields import ArrayField, HStoreField, JSONField, RangeField
except ImportError:
ArrayField, HStoreField, JSONField, RangeField = (MissingType, ) * 4
145 changes: 145 additions & 0 deletions graphene-django/graphene_django/converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
from django.db import models
from django.utils.encoding import force_text

from graphene import Enum, List, ID, Boolean, Float, Int, String, Field
from graphene.types.datetime import DateTime
from graphene.utils.str_converters import to_const
from graphene.relay import Node, ConnectionField
# from ...core.types.custom_scalars import DateTime, JSONString
from .compat import (ArrayField, HStoreField, JSONField, RangeField,
RelatedObject, UUIDField)
from .utils import get_related_model, import_single_dispatch
from .fields import DjangoConnectionField

singledispatch = import_single_dispatch()


def convert_choices(choices):
for value, name in choices:
if isinstance(name, (tuple, list)):
for choice in convert_choices(name):
yield choice
else:
yield to_const(force_text(name)), value


def convert_django_field_with_choices(field, registry=None):
choices = getattr(field, 'choices', None)
if choices:
meta = field.model._meta
name = '{}{}'.format(meta.object_name, field.name.capitalize())
graphql_choices = list(convert_choices(choices))
from collections import OrderedDict
enum = Enum(name, OrderedDict(graphql_choices))
return enum(description=field.help_text)
return convert_django_field(field, registry)


@singledispatch
def convert_django_field(field, registry=None):
raise Exception(
"Don't know how to convert the Django field %s (%s)" %
(field, field.__class__))


@convert_django_field.register(models.CharField)
@convert_django_field.register(models.TextField)
@convert_django_field.register(models.EmailField)
@convert_django_field.register(models.SlugField)
@convert_django_field.register(models.URLField)
@convert_django_field.register(models.GenericIPAddressField)
@convert_django_field.register(models.FileField)
@convert_django_field.register(UUIDField)
def convert_field_to_string(field, registry=None):
return String(description=field.help_text)


@convert_django_field.register(models.AutoField)
def convert_field_to_id(field, registry=None):
return ID(description=field.help_text)


@convert_django_field.register(models.PositiveIntegerField)
@convert_django_field.register(models.PositiveSmallIntegerField)
@convert_django_field.register(models.SmallIntegerField)
@convert_django_field.register(models.BigIntegerField)
@convert_django_field.register(models.IntegerField)
def convert_field_to_int(field, registry=None):
return Int(description=field.help_text)


@convert_django_field.register(models.BooleanField)
def convert_field_to_boolean(field, registry=None):
return Boolean(description=field.help_text, required=True)


@convert_django_field.register(models.NullBooleanField)
def convert_field_to_nullboolean(field, registry=None):
return Boolean(description=field.help_text)


@convert_django_field.register(models.DecimalField)
@convert_django_field.register(models.FloatField)
def convert_field_to_float(field, registry=None):
return Float(description=field.help_text)


@convert_django_field.register(models.DateField)
def convert_date_to_string(field, registry=None):
return DateTime(description=field.help_text)


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


@convert_django_field.register(models.ManyToManyField)
@convert_django_field.register(models.ManyToManyRel)
@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 DjangoConnectionField(_type)
return Field(List(_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 DjangoConnectionField(_type)
return Field(List(_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)


@convert_django_field.register(ArrayField)
def convert_postgres_array_to_list(field, registry=None):
base_type = convert_django_field(field.base_field)
return List(base_type, description=field.help_text)


@convert_django_field.register(HStoreField)
@convert_django_field.register(JSONField)
def convert_posgres_field_to_string(field, registry=None):
return JSONString(description=field.help_text)


@convert_django_field.register(RangeField)
def convert_posgres_range_to_string(field, registry=None):
inner_type = convert_django_field(field.base_field)
return List(inner_type, description=field.help_text)
4 changes: 4 additions & 0 deletions graphene-django/graphene_django/debug/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .middleware import DjangoDebugMiddleware
from .types import DjangoDebug

__all__ = ['DjangoDebugMiddleware', 'DjangoDebug']
56 changes: 56 additions & 0 deletions graphene-django/graphene_django/debug/middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from promise import Promise
from django.db import connections

from .sql.tracking import unwrap_cursor, wrap_cursor
from .types import DjangoDebug


class DjangoDebugContext(object):

def __init__(self):
self.debug_promise = None
self.promises = []
self.enable_instrumentation()
self.object = DjangoDebug(sql=[])

def get_debug_promise(self):
if not self.debug_promise:
self.debug_promise = Promise.all(self.promises)
return self.debug_promise.then(self.on_resolve_all_promises)

def on_resolve_all_promises(self, values):
self.disable_instrumentation()
return self.object

def add_promise(self, promise):
if self.debug_promise and not self.debug_promise.is_fulfilled:
self.promises.append(promise)

def enable_instrumentation(self):
# This is thread-safe because database connections are thread-local.
for connection in connections.all():
wrap_cursor(connection, self)

def disable_instrumentation(self):
for connection in connections.all():
unwrap_cursor(connection)


class DjangoDebugMiddleware(object):

def resolve(self, next, root, args, context, info):
django_debug = getattr(context, 'django_debug', None)
if not django_debug:
if context is None:
raise Exception('DjangoDebug cannot be executed in None contexts')
try:
context.django_debug = DjangoDebugContext()
except Exception:
raise Exception('DjangoDebug need the context to be writable, context received: {}.'.format(
context.__class__.__name__
))
if info.schema.graphene_schema.T(DjangoDebug) == info.return_type:
return context.django_debug.get_debug_promise()
promise = next(root, args, context, info)
context.django_debug.add_promise(promise)
return promise
Empty file.

0 comments on commit fcba194

Please sign in to comment.