Skip to content

Commit 90f58e7

Browse files
author
Adam Charnock
committed
Merge branch 'feature/django' into feature/django-docs
2 parents 7182aee + 58421cd commit 90f58e7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1101
-284
lines changed

.editorconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# http://editorconfig.org
2+
3+
root = true
4+
5+
[*]
6+
charset = utf-8
7+
end_of_line = lf
8+
insert_final_newline = true
9+
trim_trailing_whitespace = true
10+
11+
[*.{py,rst,ini}]
12+
indent_style = space
13+
indent_size = 4
14+

.travis.yml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ language: python
22
sudo: false
33
python:
44
- 2.7
5-
- 3.3
65
- 3.4
76
- 3.5
87
- pypy
@@ -14,8 +13,9 @@ cache:
1413
- $HOME/docs/node_modules
1514
before_install:
1615
- |
17-
if [ "$TEST_TYPE" != build_website ] && \
18-
! git diff --name-only $TRAVIS_COMMIT_RANGE | grep -qvE '(\.md$)|(^(docs))/'
16+
git_diff=$(git diff --name-only $TRAVIS_COMMIT_RANGE)
17+
if [ "$?" == 0 ] && [ "$TEST_TYPE" != build_website ] && \
18+
! echo "$git_diff" | grep -qvE '(\.md$)|(^(docs))/'
1919
then
2020
echo "Only docs were updated, stopping build process."
2121
exit
@@ -25,6 +25,7 @@ install:
2525
if [ "$TEST_TYPE" = build ]; then
2626
pip install --download-cache $HOME/.cache/pip/ pytest pytest-cov coveralls six pytest-django django-filter
2727
pip install --download-cache $HOME/.cache/pip/ -e .[django]
28+
pip install django==$DJANGO_VERSION
2829
python setup.py develop
2930
elif [ "$TEST_TYPE" = build_website ]; then
3031
pip install --download-cache $HOME/.cache/pip/ -e .
@@ -78,6 +79,14 @@ env:
7879
matrix:
7980
fast_finish: true
8081
include:
82+
- python: '2.7'
83+
env: DJANGO_VERSION=1.6
84+
- python: '2.7'
85+
env: DJANGO_VERSION=1.7
86+
- python: '2.7'
87+
env: DJANGO_VERSION=1.8
88+
- python: '2.7'
89+
env: DJANGO_VERSION=1.9
8190
- python: '2.7'
8291
env: TEST_TYPE=build_website
8392
- python: '2.7'

docs/pages/docs/basic-types.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,21 @@ graphene.Field(graphene.String(), to=graphene.String())
8282
# Is equivalent to:
8383
graphene.Field(graphene.String(), to=graphene.Argument(graphene.String()))
8484
```
85+
86+
87+
## Using custom object types as argument
88+
89+
To use a custom object type as an argument, you need to inherit `graphene.InputObjectType`, not `graphene.ObjectType`.
90+
91+
```python
92+
class CustomArgumentObjectType(graphene.InputObjectType):
93+
field1 = graphene.String()
94+
field2 = graphene.String()
95+
96+
```
97+
98+
Then, when defining this in an argument, you need to wrap it in an `Argument` object.
99+
100+
```python
101+
graphene.Field(graphene.String(), to=graphene.Argument(CustomArgumentObjectType))
102+
```

examples/starwars_django/schema.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,15 @@ class Query(graphene.ObjectType):
6363

6464
@resolve_only_args
6565
def resolve_ships(self):
66-
return [Ship(s) for s in get_ships()]
66+
return get_ships()
6767

6868
@resolve_only_args
6969
def resolve_rebels(self):
70-
return Faction(get_rebels())
70+
return get_rebels()
7171

7272
@resolve_only_args
7373
def resolve_empire(self):
74-
return Faction(get_empire())
74+
return get_empire()
7575

7676

7777
class Mutation(graphene.ObjectType):

graphene/__init__.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,14 @@
44

55
from graphene import signals
66

7-
from graphene.core.schema import (
8-
Schema
9-
)
10-
11-
from graphene.core.classtypes import (
7+
from .core import (
8+
Schema,
129
ObjectType,
1310
InputObjectType,
1411
Interface,
1512
Mutation,
16-
Scalar
17-
)
18-
19-
from graphene.core.types import (
20-
BaseType,
13+
Scalar,
14+
InstanceType,
2115
LazyType,
2216
Argument,
2317
Field,
@@ -57,7 +51,7 @@
5751
'NonNull',
5852
'signals',
5953
'Schema',
60-
'BaseType',
54+
'InstanceType',
6155
'LazyType',
6256
'ObjectType',
6357
'InputObjectType',

graphene/contrib/django/compat.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from django.db import models
2+
3+
try:
4+
UUIDField = models.UUIDField
5+
except AttributeError:
6+
# Improved compatibility for Django 1.6
7+
class UUIDField(object):
8+
pass
9+
10+
try:
11+
from django.db.models.related import RelatedObject
12+
except:
13+
# Improved compatibility for Django 1.6
14+
class RelatedObject(object):
15+
pass

graphene/contrib/django/converter.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
from django.db import models
2-
from singledispatch import singledispatch
32

43
from ...core.types.scalars import ID, Boolean, Float, Int, String
4+
from .compat import RelatedObject, UUIDField
5+
from .utils import get_related_model, import_single_dispatch
56

6-
try:
7-
UUIDField = models.UUIDField
8-
except AttributeError:
9-
# Improved compatibility for Django 1.6
10-
class UUIDField(object):
11-
pass
7+
singledispatch = import_single_dispatch()
128

139

1410
@singledispatch
@@ -24,6 +20,7 @@ def convert_django_field(field):
2420
@convert_django_field.register(models.EmailField)
2521
@convert_django_field.register(models.SlugField)
2622
@convert_django_field.register(models.URLField)
23+
@convert_django_field.register(models.GenericIPAddressField)
2724
@convert_django_field.register(UUIDField)
2825
def convert_field_to_string(field):
2926
return String(description=field.help_text)
@@ -63,12 +60,20 @@ def convert_field_to_float(field):
6360
@convert_django_field.register(models.ManyToOneRel)
6461
def convert_field_to_list_or_connection(field):
6562
from .fields import DjangoModelField, ConnectionOrListField
66-
model_field = DjangoModelField(field.related_model)
63+
model_field = DjangoModelField(get_related_model(field))
64+
return ConnectionOrListField(model_field)
65+
66+
67+
# For Django 1.6
68+
@convert_django_field.register(RelatedObject)
69+
def convert_relatedfield_to_djangomodel(field):
70+
from .fields import DjangoModelField, ConnectionOrListField
71+
model_field = DjangoModelField(field.model)
6772
return ConnectionOrListField(model_field)
6873

6974

7075
@convert_django_field.register(models.OneToOneField)
7176
@convert_django_field.register(models.ForeignKey)
7277
def convert_field_to_djangomodel(field):
7378
from .fields import DjangoModelField
74-
return DjangoModelField(field.related_model, description=field.help_text)
79+
return DjangoModelField(get_related_model(field), description=field.help_text)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .plugin import DjangoDebugPlugin
2+
from .types import DjangoDebug
3+
4+
__all__ = ['DjangoDebugPlugin', 'DjangoDebug']
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from contextlib import contextmanager
2+
3+
from django.db import connections
4+
5+
from ....core.schema import GraphQLSchema
6+
from ....core.types import Field
7+
from ....plugins import Plugin
8+
from .sql.tracking import unwrap_cursor, wrap_cursor
9+
from .sql.types import DjangoDebugSQL
10+
from .types import DjangoDebug
11+
12+
13+
class WrappedRoot(object):
14+
15+
def __init__(self, root):
16+
self._recorded = []
17+
self._root = root
18+
19+
def record(self, **log):
20+
self._recorded.append(DjangoDebugSQL(**log))
21+
22+
def debug(self):
23+
return DjangoDebug(sql=self._recorded)
24+
25+
26+
class WrapRoot(object):
27+
28+
@property
29+
def _root(self):
30+
return self._wrapped_root.root
31+
32+
@_root.setter
33+
def _root(self, value):
34+
self._wrapped_root = value
35+
36+
def resolve_debug(self, args, info):
37+
return self._wrapped_root.debug()
38+
39+
40+
def debug_objecttype(objecttype):
41+
return type(
42+
'Debug{}'.format(objecttype._meta.type_name),
43+
(WrapRoot, objecttype),
44+
{'debug': Field(DjangoDebug, name='__debug')})
45+
46+
47+
class DjangoDebugPlugin(Plugin):
48+
49+
def enable_instrumentation(self, wrapped_root):
50+
# This is thread-safe because database connections are thread-local.
51+
for connection in connections.all():
52+
wrap_cursor(connection, wrapped_root)
53+
54+
def disable_instrumentation(self):
55+
for connection in connections.all():
56+
unwrap_cursor(connection)
57+
58+
def wrap_schema(self, schema_type):
59+
query = schema_type._query
60+
if query:
61+
class_type = self.schema.objecttype(schema_type.get_query_type())
62+
assert class_type, 'The query in schema is not constructed with graphene'
63+
_type = debug_objecttype(class_type)
64+
self.schema.register(_type, force=True)
65+
return GraphQLSchema(
66+
self.schema,
67+
self.schema.T(_type),
68+
schema_type.get_mutation_type(),
69+
schema_type.get_subscription_type()
70+
)
71+
return schema_type
72+
73+
@contextmanager
74+
def context_execution(self, executor):
75+
executor['root'] = WrappedRoot(root=executor['root'])
76+
executor['schema'] = self.wrap_schema(executor['schema'])
77+
self.enable_instrumentation(executor['root'])
78+
yield executor
79+
self.disable_instrumentation()

0 commit comments

Comments
 (0)