Skip to content

Commit 332d7b0

Browse files
committed
Merge pull request #140 from graphql-python/features/django-fields
Improved support for Django fields
2 parents ae23a11 + 91cc0ce commit 332d7b0

File tree

22 files changed

+268
-84
lines changed

22 files changed

+268
-84
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ install:
2424
- |
2525
if [ "$TEST_TYPE" = build ]; then
2626
pip install --download-cache $HOME/.cache/pip/ pytest pytest-cov coveralls six pytest-django django-filter sqlalchemy_utils
27+
pip install --download-cache $HOME/.cache/pip psycopg2 > /dev/null 2>&1
2728
pip install --download-cache $HOME/.cache/pip/ -e .[django]
2829
pip install --download-cache $HOME/.cache/pip/ -e .[sqlalchemy]
2930
pip install django==$DJANGO_VERSION

docs/pages/docs/basic-types.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ Also the following Types are available:
1616
- `graphene.List`
1717
- `graphene.NonNull`
1818

19+
Graphene also provides custom scalars for Dates and JSON:
20+
- `graphene.core.types.custom_scalars.DateTime`
21+
- `graphene.core.types.custom_scalars.JSONString`
22+
1923
## Shortcuts
2024

2125
There are some shortcuts for building schemas more easily.

examples/flask_sqlalchemy/app.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from flask import Flask
2-
from database import db_session, init_db
32

4-
from schema import schema
3+
from database import db_session, init_db
54
from flask_graphql import GraphQL
5+
from schema import schema
66

77
app = Flask(__name__)
88
app.debug = True

examples/flask_sqlalchemy/database.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from sqlalchemy import create_engine
2-
from sqlalchemy.orm import scoped_session, sessionmaker
32
from sqlalchemy.ext.declarative import declarative_base
3+
from sqlalchemy.orm import scoped_session, sessionmaker
44

55
engine = create_engine('sqlite:///database.sqlite3', convert_unicode=True)
66
db_session = scoped_session(sessionmaker(autocommit=False,

examples/flask_sqlalchemy/models.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, func
2+
from sqlalchemy.orm import backref, relationship
3+
14
from database import Base
2-
from sqlalchemy import Column, DateTime, String, Integer, ForeignKey, func
3-
from sqlalchemy.orm import relationship, backref
45

56

67
class Department(Base):

examples/flask_sqlalchemy/schema.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
import graphene
22
from graphene import relay
3-
from graphene.contrib.sqlalchemy import SQLAlchemyNode, SQLAlchemyConnectionField
4-
from models import Department as DepartmentModel, Employee as EmployeeModel
3+
from graphene.contrib.sqlalchemy import (SQLAlchemyConnectionField,
4+
SQLAlchemyNode)
5+
from models import Department as DepartmentModel
6+
from models import Employee as EmployeeModel
57

68
schema = graphene.Schema()
79

810

911
@schema.register
1012
class Department(SQLAlchemyNode):
13+
1114
class Meta:
1215
model = DepartmentModel
1316

1417

1518
@schema.register
1619
class Employee(SQLAlchemyNode):
20+
1721
class Meta:
1822
model = EmployeeModel
1923

graphene/contrib/django/compat.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
from django.db import models
22

3+
4+
class MissingType(object):
5+
pass
6+
37
try:
48
UUIDField = models.UUIDField
59
except AttributeError:
610
# Improved compatibility for Django 1.6
7-
class UUIDField(object):
8-
pass
11+
UUIDField = MissingType
912

1013
try:
1114
from django.db.models.related import RelatedObject
1215
except:
1316
# Improved compatibility for Django 1.6
14-
class RelatedObject(object):
15-
pass
17+
RelatedObject = MissingType
18+
19+
20+
try:
21+
# Postgres fields are only available in Django 1.8+
22+
from django.contrib.postgres.fields import ArrayField, HStoreField, JSONField, RangeField
23+
except ImportError:
24+
ArrayField, HStoreField, JSONField, RangeField = (MissingType, ) * 4

graphene/contrib/django/converter.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
from django.db import models
22

3-
from ...core.types.scalars import ID, Boolean, Float, Int, String
43
from ...core.classtypes.enum import Enum
4+
from ...core.types.custom_scalars import DateTime, JSONString
5+
from ...core.types.definitions import List
6+
from ...core.types.scalars import ID, Boolean, Float, Int, String
57
from ...utils import to_const
6-
from .compat import RelatedObject, UUIDField
8+
from .compat import (ArrayField, HStoreField, JSONField, RangeField,
9+
RelatedObject, UUIDField)
710
from .utils import get_related_model, import_single_dispatch
811

912
singledispatch = import_single_dispatch()
@@ -30,13 +33,13 @@ def convert_django_field(field):
3033
(field, field.__class__))
3134

3235

33-
@convert_django_field.register(models.DateField)
3436
@convert_django_field.register(models.CharField)
3537
@convert_django_field.register(models.TextField)
3638
@convert_django_field.register(models.EmailField)
3739
@convert_django_field.register(models.SlugField)
3840
@convert_django_field.register(models.URLField)
3941
@convert_django_field.register(models.GenericIPAddressField)
42+
@convert_django_field.register(models.FileField)
4043
@convert_django_field.register(UUIDField)
4144
def convert_field_to_string(field):
4245
return String(description=field.help_text)
@@ -72,6 +75,11 @@ def convert_field_to_float(field):
7275
return Float(description=field.help_text)
7376

7477

78+
@convert_django_field.register(models.DateField)
79+
def convert_date_to_string(field):
80+
return DateTime(description=field.help_text)
81+
82+
7583
@convert_django_field.register(models.ManyToManyField)
7684
@convert_django_field.register(models.ManyToOneRel)
7785
@convert_django_field.register(models.ManyToManyRel)
@@ -94,3 +102,21 @@ def convert_relatedfield_to_djangomodel(field):
94102
def convert_field_to_djangomodel(field):
95103
from .fields import DjangoModelField
96104
return DjangoModelField(get_related_model(field), description=field.help_text)
105+
106+
107+
@convert_django_field.register(ArrayField)
108+
def convert_postgres_array_to_list(field):
109+
base_type = convert_django_field(field.base_field)
110+
return List(base_type, description=field.help_text)
111+
112+
113+
@convert_django_field.register(HStoreField)
114+
@convert_django_field.register(JSONField)
115+
def convert_posgres_field_to_string(field):
116+
return JSONString(description=field.help_text)
117+
118+
119+
@convert_django_field.register(RangeField)
120+
def convert_posgres_range_to_string(field):
121+
inner_type = convert_django_field(field.base_field)
122+
return List(inner_type, description=field.help_text)

graphene/contrib/django/debug/tests/test_query.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytest
22

33
import graphene
4-
from graphene.contrib.django import DjangoNode, DjangoConnectionField
4+
from graphene.contrib.django import DjangoConnectionField, DjangoNode
55
from graphene.contrib.django.utils import DJANGO_FILTER_INSTALLED
66

77
from ...tests.models import Reporter
@@ -19,6 +19,7 @@ def test_should_query_field():
1919
r2.save()
2020

2121
class ReporterType(DjangoNode):
22+
2223
class Meta:
2324
model = Reporter
2425

graphene/contrib/django/filter/filterset.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
from django.conf import settings
33
from django.db import models
44
from django.utils.text import capfirst
5-
from graphql_relay.node.node import from_global_id
6-
75
from django_filters import Filter, MultipleChoiceFilter
86
from django_filters.filterset import FilterSet, FilterSetMetaclass
7+
from graphql_relay.node.node import from_global_id
8+
99
from graphene.contrib.django.forms import (GlobalIDFormField,
1010
GlobalIDMultipleChoiceField)
1111

0 commit comments

Comments
 (0)