Skip to content

Commit 0868497

Browse files
committed
Merge branch with 'master'
2 parents ef82e79 + d5e71bc commit 0868497

Some content is hidden

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

43 files changed

+1032
-137
lines changed

Makefile

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
1+
.PHONY: dev-setup ## Install development dependencies
12
dev-setup:
23
pip install -e ".[dev]"
34

5+
.PHONY: install-dev
6+
install-dev: dev-setup # Alias install-dev -> dev-setup
7+
8+
.PHONY: tests
49
tests:
510
py.test graphene_django --cov=graphene_django -vv
611

12+
.PHONY: test
13+
test: tests # Alias test -> tests
14+
15+
.PHONY: format
716
format:
8-
black --exclude "/migrations/" graphene_django examples
17+
black --exclude "/migrations/" graphene_django examples setup.py
918

19+
.PHONY: lint
1020
lint:
1121
flake8 graphene_django examples
22+
23+
.PHONY: docs ## Generate docs
24+
docs: dev-setup
25+
cd docs && make install && make html
26+
27+
.PHONY: docs-live ## Generate docs with live reloading
28+
docs-live: dev-setup
29+
cd docs && make install && make livehtml

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@ GRAPHENE = {
3838
We need to set up a `GraphQL` endpoint in our Django app, so we can serve the queries.
3939

4040
```python
41-
from django.conf.urls import url
41+
from django.urls import path
4242
from graphene_django.views import GraphQLView
4343

4444
urlpatterns = [
4545
# ...
46-
url(r'^graphql$', GraphQLView.as_view(graphiql=True)),
46+
path('graphql', GraphQLView.as_view(graphiql=True)),
4747
]
4848
```
4949

@@ -100,4 +100,4 @@ To learn more check out the following [examples](examples/):
100100

101101
## Contributing
102102

103-
See [CONTRIBUTING.md](CONTRIBUTING.md)
103+
See [CONTRIBUTING.md](CONTRIBUTING.md)

docs/Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,20 @@ help:
4848
clean:
4949
rm -rf $(BUILDDIR)/*
5050

51+
.PHONY: install ## to install all documentation related requirements
52+
install:
53+
pip install -r requirements.txt
54+
5155
.PHONY: html
5256
html:
5357
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
5458
@echo
5559
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
5660

61+
.PHONY: livehtml ## to build and serve live-reloading documentation
62+
livehtml:
63+
sphinx-autobuild -b html --watch ../graphene_django $(ALLSPHINXOPTS) $(BUILDDIR)/html
64+
5765
.PHONY: dirhtml
5866
dirhtml:
5967
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml

docs/_static/.gitkeep

Whitespace-only changes.

docs/authorization.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ Adding Login Required
154154
To restrict users from accessing the GraphQL API page the standard Django LoginRequiredMixin_ can be used to create your own standard Django Class Based View, which includes the ``LoginRequiredMixin`` and subclasses the ``GraphQLView``.:
155155

156156
.. code:: python
157-
#views.py
157+
158+
# views.py
158159
159160
from django.contrib.auth.mixins import LoginRequiredMixin
160161
from graphene_django.views import GraphQLView

docs/mutations.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ customize the look up with the ``lookup_field`` attribute on the ``SerializerMut
151151
.. code:: python
152152
153153
from graphene_django.rest_framework.mutation import SerializerMutation
154-
from .serializers imoprt MyModelSerializer
154+
from .serializers import MyModelSerializer
155155
156156
157157
class AwesomeModelMutation(SerializerMutation):
@@ -168,7 +168,7 @@ Use the method ``get_serializer_kwargs`` to override how updates are applied.
168168
.. code:: python
169169
170170
from graphene_django.rest_framework.mutation import SerializerMutation
171-
from .serializers imoprt MyModelSerializer
171+
from .serializers import MyModelSerializer
172172
173173
174174
class AwesomeModelMutation(SerializerMutation):
@@ -199,7 +199,7 @@ You can use relay with mutations. A Relay mutation must inherit from
199199

200200
.. code:: python
201201
202-
import graphene
202+
import graphene
203203
from graphene import relay
204204
from graphene_django import DjangoObjectType
205205
from graphql_relay import from_global_id

docs/queries.rst

Lines changed: 94 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,18 @@ Full example
4141
return Question.objects.get(pk=question_id)
4242
4343
44-
Fields
45-
------
44+
Specifying which fields to include
45+
----------------------------------
4646

4747
By default, ``DjangoObjectType`` will present all fields on a Model through GraphQL.
48-
If you don't want to do this you can change this by setting either ``only_fields`` and ``exclude_fields``.
48+
If you only want a subset of fields to be present, you can do so using
49+
``fields`` or ``exclude``. It is strongly recommended that you explicitly set
50+
all fields that should be exposed using the fields attribute.
51+
This will make it less likely to result in unintentionally exposing data when
52+
your models change.
4953

50-
only_fields
51-
~~~~~~~~~~~
54+
``fields``
55+
~~~~~~~~~~
5256

5357
Show **only** these fields on the model:
5458

@@ -57,24 +61,35 @@ Show **only** these fields on the model:
5761
class QuestionType(DjangoObjectType):
5862
class Meta:
5963
model = Question
60-
only_fields = ('question_text')
64+
fields = ('id', 'question_text')
65+
66+
You can also set the ``fields`` attribute to the special value ``'__all__'`` to indicate that all fields in the model should be used.
6167

68+
For example:
6269

63-
exclude_fields
64-
~~~~~~~~~~~~~~
70+
.. code:: python
6571
66-
Show all fields **except** those in ``exclude_fields``:
72+
class QuestionType(DjangoObjectType):
73+
class Meta:
74+
model = Question
75+
fields = '__all__'
76+
77+
78+
``exclude``
79+
~~~~~~~~~~~
80+
81+
Show all fields **except** those in ``exclude``:
6782

6883
.. code:: python
6984
7085
class QuestionType(DjangoObjectType):
7186
class Meta:
7287
model = Question
73-
exclude_fields = ('question_text')
88+
exclude = ('question_text',)
7489
7590
76-
Customised fields
77-
~~~~~~~~~~~~~~~~~
91+
Customising fields
92+
------------------
7893

7994
You can completely overwrite a field, or add new fields, to a ``DjangoObjectType`` using a Resolver:
8095

@@ -84,14 +99,79 @@ You can completely overwrite a field, or add new fields, to a ``DjangoObjectType
8499
85100
class Meta:
86101
model = Question
87-
exclude_fields = ('question_text')
102+
fields = ('id', 'question_text')
88103
89104
extra_field = graphene.String()
90105
91106
def resolve_extra_field(self, info):
92107
return 'hello!'
93108
94109
110+
Choices to Enum conversion
111+
~~~~~~~~~~~~~~~~~~~~~~~~~~
112+
113+
By default Graphene-Django will convert any Django fields that have `choices`_
114+
defined into a GraphQL enum type.
115+
116+
.. _choices: https://docs.djangoproject.com/en/2.2/ref/models/fields/#choices
117+
118+
For example the following ``Model`` and ``DjangoObjectType``:
119+
120+
.. code:: python
121+
122+
class PetModel(models.Model):
123+
kind = models.CharField(max_length=100, choices=(('cat', 'Cat'), ('dog', 'Dog')))
124+
125+
class Pet(DjangoObjectType):
126+
class Meta:
127+
model = PetModel
128+
129+
Results in the following GraphQL schema definition:
130+
131+
.. code::
132+
133+
type Pet {
134+
id: ID!
135+
kind: PetModelKind!
136+
}
137+
138+
enum PetModelKind {
139+
CAT
140+
DOG
141+
}
142+
143+
You can disable this automatic conversion by setting
144+
``convert_choices_to_enum`` attribute to ``False`` on the ``DjangoObjectType``
145+
``Meta`` class.
146+
147+
.. code:: python
148+
149+
class Pet(DjangoObjectType):
150+
class Meta:
151+
model = PetModel
152+
convert_choices_to_enum = False
153+
154+
.. code::
155+
156+
type Pet {
157+
id: ID!
158+
kind: String!
159+
}
160+
161+
You can also set ``convert_choices_to_enum`` to a list of fields that should be
162+
automatically converted into enums:
163+
164+
.. code:: python
165+
166+
class Pet(DjangoObjectType):
167+
class Meta:
168+
model = PetModel
169+
convert_choices_to_enum = ['kind']
170+
171+
**Note:** Setting ``convert_choices_to_enum = []`` is the same as setting it to
172+
``False``.
173+
174+
95175
Related models
96176
--------------
97177

@@ -113,7 +193,7 @@ When ``Question`` is published as a ``DjangoObjectType`` and you want to add ``C
113193
class QuestionType(DjangoObjectType):
114194
class Meta:
115195
model = Question
116-
only_fields = ('category',)
196+
fields = ('category',)
117197
118198
Then all query-able related models must be defined as DjangoObjectType subclass,
119199
or they will fail to show if you are trying to query those relation fields. You only

docs/requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
sphinx
1+
Sphinx==1.5.3
2+
sphinx-autobuild==0.7.1
23
# Docs template
34
http://graphene-python.org/sphinx_graphene_theme.zip

docs/settings.rst

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Default: ``None``
3030
3131
3232
``SCHEMA_OUTPUT``
33-
----------
33+
-----------------
3434

3535
The name of the file where the GraphQL schema output will go.
3636

@@ -44,7 +44,7 @@ Default: ``schema.json``
4444
4545
4646
``SCHEMA_INDENT``
47-
----------
47+
-----------------
4848

4949
The indentation level of the schema output.
5050

@@ -58,7 +58,7 @@ Default: ``2``
5858
5959
6060
``MIDDLEWARE``
61-
----------
61+
--------------
6262

6363
A tuple of middleware that will be executed for each GraphQL query.
6464

@@ -76,7 +76,7 @@ Default: ``()``
7676
7777
7878
``RELAY_CONNECTION_ENFORCE_FIRST_OR_LAST``
79-
----------
79+
------------------------------------------
8080

8181
Enforces relay queries to have the ``first`` or ``last`` argument.
8282

@@ -90,7 +90,7 @@ Default: ``False``
9090
9191
9292
``RELAY_CONNECTION_MAX_LIMIT``
93-
----------
93+
------------------------------
9494

9595
The maximum size of objects that can be requested through a relay connection.
9696

@@ -101,3 +101,42 @@ Default: ``100``
101101
GRAPHENE = {
102102
'RELAY_CONNECTION_MAX_LIMIT': 100,
103103
}
104+
105+
106+
``CAMELCASE_ERRORS``
107+
------------------------------------
108+
109+
When set to ``True`` field names in the ``errors`` object will be camel case.
110+
By default they will be snake case.
111+
112+
Default: ``False``
113+
114+
.. code:: python
115+
116+
GRAPHENE = {
117+
'CAMELCASE_ERRORS': False,
118+
}
119+
120+
# result = schema.execute(...)
121+
print(result.errors)
122+
# [
123+
# {
124+
# 'field': 'test_field',
125+
# 'messages': ['This field is required.'],
126+
# }
127+
# ]
128+
129+
.. code:: python
130+
131+
GRAPHENE = {
132+
'CAMELCASE_ERRORS': True,
133+
}
134+
135+
# result = schema.execute(...)
136+
print(result.errors)
137+
# [
138+
# {
139+
# 'field': 'testField',
140+
# 'messages': ['This field is required.'],
141+
# }
142+
# ]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
graphene
22
graphene-django
33
graphql-core>=2.1rc1
4-
django==2.1.6
4+
django==2.2.4

examples/cookbook/cookbook/ingredients/models.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ def __str__(self):
1111
class Ingredient(models.Model):
1212
name = models.CharField(max_length=100)
1313
notes = models.TextField(null=True, blank=True)
14-
category = models.ForeignKey(Category, related_name="ingredients")
14+
category = models.ForeignKey(
15+
Category, related_name="ingredients", on_delete=models.CASCADE
16+
)
1517

1618
def __str__(self):
1719
return self.name

examples/cookbook/cookbook/recipes/models.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ class Recipe(models.Model):
1010

1111

1212
class RecipeIngredient(models.Model):
13-
recipe = models.ForeignKey(Recipe, related_name="amounts")
14-
ingredient = models.ForeignKey(Ingredient, related_name="used_by")
13+
recipe = models.ForeignKey(Recipe, related_name="amounts", on_delete=models.CASCADE)
14+
ingredient = models.ForeignKey(
15+
Ingredient, related_name="used_by", on_delete=models.CASCADE
16+
)
1517
amount = models.FloatField()
1618
unit = models.CharField(
1719
max_length=20,

0 commit comments

Comments
 (0)