Rebuild documentation

This commit is contained in:
Paul Hallett 2019-04-26 16:48:37 +01:00
parent 6f03597a5e
commit 31468f5687
No known key found for this signature in database
GPG Key ID: 529C11F0C93CDF11
17 changed files with 766 additions and 213 deletions

View File

@ -7,7 +7,11 @@ Please read [UPGRADE-v2.0.md](https://github.com/graphql-python/graphene/blob/ma
A [Django](https://www.djangoproject.com/) integration for [Graphene](http://graphene-python.org/). A [Django](https://www.djangoproject.com/) integration for [Graphene](http://graphene-python.org/).
## Installation ## Documentation
[Visit the documentation to get started!](https://docs.graphene-python.org/projects/django/en/latest/)
## Quickstart
For installing graphene, just run this command in your shell For installing graphene, just run this command in your shell
@ -39,7 +43,7 @@ from graphene_django.views import GraphQLView
urlpatterns = [ urlpatterns = [
# ... # ...
url(r'^graphql', GraphQLView.as_view(graphiql=True)), url(r'^graphql$', GraphQLView.as_view(graphiql=True)),
] ]
``` ```

View File

@ -10,8 +10,14 @@ to learn how to upgrade to Graphene ``2.0``.
A `Django <https://www.djangoproject.com/>`__ integration for A `Django <https://www.djangoproject.com/>`__ integration for
`Graphene <http://graphene-python.org/>`__. `Graphene <http://graphene-python.org/>`__.
Installation
------------ Documentation
-------------
`Visit the documentation to get started! <https://docs.graphene-python.org/projects/django/en/latest/>`__
Quickstart
----------
For installing graphene, just run this command in your shell For installing graphene, just run this command in your shell
@ -46,7 +52,7 @@ serve the queries.
urlpatterns = [ urlpatterns = [
# ... # ...
url(r'^graphql', GraphQLView.as_view(graphiql=True)), url(r'^graphql$', GraphQLView.as_view(graphiql=True)),
] ]
Examples Examples

View File

@ -155,7 +155,7 @@ To restrict users from accessing the GraphQL API page the standard Django LoginR
.. code:: python .. code:: python
#views.py #views.py
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from graphene_django.views import GraphQLView from graphene_django.views import GraphQLView
@ -171,9 +171,9 @@ For Django 1.9 and below:
urlpatterns = [ urlpatterns = [
# some other urls # some other urls
url(r'^graphql', PrivateGraphQLView.as_view(graphiql=True, schema=schema)), url(r'^graphql$', PrivateGraphQLView.as_view(graphiql=True, schema=schema)),
] ]
For Django 2.0 and above: For Django 2.0 and above:
.. code:: python .. code:: python

View File

@ -1,6 +1,6 @@
import os import os
on_rtd = os.environ.get('READTHEDOCS', None) == 'True' on_rtd = os.environ.get("READTHEDOCS", None) == "True"
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
@ -34,46 +34,44 @@ on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones. # ones.
extensions = [ extensions = [
'sphinx.ext.autodoc', "sphinx.ext.autodoc",
'sphinx.ext.intersphinx', "sphinx.ext.intersphinx",
'sphinx.ext.todo', "sphinx.ext.todo",
'sphinx.ext.coverage', "sphinx.ext.coverage",
'sphinx.ext.viewcode', "sphinx.ext.viewcode",
] ]
if not on_rtd: if not on_rtd:
extensions += [ extensions += ["sphinx.ext.githubpages"]
'sphinx.ext.githubpages',
]
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates'] templates_path = ["_templates"]
# The suffix(es) of source filenames. # The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string: # You can specify multiple suffix as a list of string:
# #
# source_suffix = ['.rst', '.md'] # source_suffix = ['.rst', '.md']
source_suffix = '.rst' source_suffix = ".rst"
# The encoding of source files. # The encoding of source files.
# #
# source_encoding = 'utf-8-sig' # source_encoding = 'utf-8-sig'
# The master toctree document. # The master toctree document.
master_doc = 'index' master_doc = "index"
# General information about the project. # General information about the project.
project = u'Graphene Django' project = u"Graphene Django"
copyright = u'Graphene 2017' copyright = u"Graphene 2017"
author = u'Syrus Akbary' author = u"Syrus Akbary"
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = u'1.0' version = u"1.0"
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = u'1.0.dev' release = u"1.0.dev"
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.
@ -94,7 +92,7 @@ language = None
# List of patterns, relative to source directory, that match files and # List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files. # directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path # This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
# The reST default role (used for this markup: `text`) to use for all # The reST default role (used for this markup: `text`) to use for all
# documents. # documents.
@ -116,7 +114,7 @@ exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# show_authors = False # show_authors = False
# The name of the Pygments (syntax highlighting) style to use. # The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx' pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting. # A list of ignored prefixes for module index sorting.
# modindex_common_prefix = [] # modindex_common_prefix = []
@ -175,7 +173,7 @@ html_theme_path = [sphinx_graphene_theme.get_html_theme_path()]
# Add any paths that contain custom static files (such as style sheets) here, # Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files, # relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css". # so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static'] html_static_path = ["_static"]
# Add any extra paths that contain custom files (such as robots.txt or # Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied # .htaccess) here, relative to this directory. These files are copied
@ -255,34 +253,30 @@ html_static_path = ['_static']
# html_search_scorer = 'scorer.js' # html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder. # Output file base name for HTML help builder.
htmlhelp_basename = 'Graphenedoc' htmlhelp_basename = "Graphenedoc"
# -- Options for LaTeX output --------------------------------------------- # -- Options for LaTeX output ---------------------------------------------
latex_elements = { latex_elements = {
# The paper size ('letterpaper' or 'a4paper'). # The paper size ('letterpaper' or 'a4paper').
# #
# 'papersize': 'letterpaper', # 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
# The font size ('10pt', '11pt' or '12pt'). #
# # 'pointsize': '10pt',
# 'pointsize': '10pt', # Additional stuff for the LaTeX preamble.
#
# Additional stuff for the LaTeX preamble. # 'preamble': '',
# # Latex figure (float) alignment
# 'preamble': '', #
# 'figure_align': 'htbp',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
} }
# Grouping the document tree into LaTeX files. List of tuples # Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, # (source start file, target name, title,
# author, documentclass [howto, manual, or own class]). # author, documentclass [howto, manual, or own class]).
latex_documents = [ latex_documents = [
(master_doc, 'Graphene.tex', u'Graphene Documentation', (master_doc, "Graphene.tex", u"Graphene Documentation", u"Syrus Akbary", "manual")
u'Syrus Akbary', 'manual'),
] ]
# The name of an image file (relative to this directory) to place at the top of # The name of an image file (relative to this directory) to place at the top of
@ -323,8 +317,7 @@ latex_documents = [
# One entry per manual page. List of tuples # One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section). # (source start file, name, description, authors, manual section).
man_pages = [ man_pages = [
(master_doc, 'graphene_django', u'Graphene Django Documentation', (master_doc, "graphene_django", u"Graphene Django Documentation", [author], 1)
[author], 1)
] ]
# If true, show URL addresses after external links. # If true, show URL addresses after external links.
@ -338,9 +331,15 @@ man_pages = [
# (source start file, target name, title, author, # (source start file, target name, title, author,
# dir menu entry, description, category) # dir menu entry, description, category)
texinfo_documents = [ texinfo_documents = [
(master_doc, 'Graphene-Django', u'Graphene Django Documentation', (
author, 'Graphene Django', 'One line description of project.', master_doc,
'Miscellaneous'), "Graphene-Django",
u"Graphene Django Documentation",
author,
"Graphene Django",
"One line description of project.",
"Miscellaneous",
)
] ]
# Documents to append as an appendix to all manuals. # Documents to append as an appendix to all manuals.
@ -414,7 +413,7 @@ epub_copyright = copyright
# epub_post_files = [] # epub_post_files = []
# A list of files that should not be packed into the epub file. # A list of files that should not be packed into the epub file.
epub_exclude_files = ['search.html'] epub_exclude_files = ["search.html"]
# The depth of the table of contents in toc.ncx. # The depth of the table of contents in toc.ncx.
# #
@ -446,4 +445,4 @@ epub_exclude_files = ['search.html']
# Example configuration for intersphinx: refer to the Python standard library. # Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'https://docs.python.org/': None} intersphinx_mapping = {"https://docs.python.org/": None}

View File

@ -34,6 +34,7 @@ And in your ``settings.py``:
.. code:: python .. code:: python
GRAPHENE = { GRAPHENE = {
...
'MIDDLEWARE': [ 'MIDDLEWARE': [
'graphene_django.debug.DjangoDebugMiddleware', 'graphene_django.debug.DjangoDebugMiddleware',
] ]

View File

@ -136,7 +136,7 @@ pre-filter animals owned by the authenticated user (set in ``context.user``).
class AnimalFilter(django_filters.FilterSet): class AnimalFilter(django_filters.FilterSet):
# Do case-insensitive lookups on 'name' # Do case-insensitive lookups on 'name'
name = django_filters.CharFilter(lookup_type='iexact') name = django_filters.CharFilter(lookup_type=['iexact'])
class Meta: class Meta:
model = Animal model = Animal
@ -146,3 +146,49 @@ pre-filter animals owned by the authenticated user (set in ``context.user``).
def qs(self): def qs(self):
# The query context can be found in self.request. # The query context can be found in self.request.
return super(AnimalFilter, self).qs.filter(owner=self.request.user) return super(AnimalFilter, self).qs.filter(owner=self.request.user)
Ordering
--------
You can use ``OrderFilter`` to define how you want your returned results to be ordered.
Extend the tuple of fields if you want to order by more than one field.
.. code:: python
from django_filters import FilterSet, OrderingFilter
class UserFilter(FilterSet):
class Meta:
model = UserModel
order_by = OrderingFilter(
fields=(
('created_at', 'created_at'),
)
)
class Group(DjangoObjectType):
users = DjangoFilterConnectionField(Ticket, filterset_class=UserFilter)
class Meta:
name = 'Group'
model = GroupModel
interfaces = (relay.Node,)
def resolve_users(self, info, **kwargs):
return UserFilter(kwargs).qs
with this set up, you can now order the users under group:
.. code::
query {
group(id: "xxx") {
users(orderBy: "-created_at") {
xxx
}
}
}

View File

@ -1,74 +0,0 @@
Integration with Django forms
=============================
Graphene-Django comes with mutation classes that will convert the fields on Django forms into inputs on a mutation.
*Note: the API is experimental and will likely change in the future.*
DjangoFormMutation
------------------
.. code:: python
from graphene_django.forms.mutation import DjangoFormMutation
class MyForm(forms.Form):
name = forms.CharField()
class MyMutation(DjangoFormMutation):
class Meta:
form_class = MyForm
``MyMutation`` will automatically receive an ``input`` argument. This argument should be a ``dict`` where the key is ``name`` and the value is a string.
DjangoModelFormMutation
-----------------------
``DjangoModelFormMutation`` will pull the fields from a ``ModelForm``.
.. code:: python
from graphene_django.forms.mutation import DjangoModelFormMutation
class Pet(models.Model):
name = models.CharField()
class PetForm(forms.ModelForm):
class Meta:
model = Pet
fields = ('name',)
# This will get returned when the mutation completes successfully
class PetType(DjangoObjectType):
class Meta:
model = Pet
class PetMutation(DjangoModelFormMutation):
pet = Field(PetType)
class Meta:
form_class = PetForm
``PetMutation`` will grab the fields from ``PetForm`` and turn them into inputs. If the form is valid then the mutation
will lookup the ``DjangoObjectType`` for the ``Pet`` model and return that under the key ``pet``. Otherwise it will
return a list of errors.
You can change the input name (default is ``input``) and the return field name (default is the model name lowercase).
.. code:: python
class PetMutation(DjangoModelFormMutation):
class Meta:
form_class = PetForm
input_field_name = 'data'
return_field_name = 'my_pet'
Form validation
---------------
Form mutations will call ``is_valid()`` on your forms.
If the form is valid then the class method ``perform_mutate(form, info)`` is called on the mutation. Override this method
to change how the form is saved or to return a different Graphene object type.
If the form is *not* valid then a list of errors will be returned. These errors have two fields: ``field``, a string
containing the name of the invalid form field, and ``messages``, a list of strings with the validation messages.

View File

@ -1,17 +1,34 @@
Graphene-Django Graphene-Django
=============== ===============
Contents: Welcome to the Graphene-Django docs.
Graphene-Django is built on top of `Graphene <https://docs.graphene-python.org/en/latest/>`__.
Graphene-Django provides some additional abstractions that make it easy to add GraphQL functionality to your Django project.
First time? We recommend you start with the installation guide to get set up and the basic tutorial.
It is worth reading the `core graphene docs <https://docs.graphene-python.org/en/latest/>`__ to familiarize yourself with the basic utilities.
Core tenants
------------
If you want to expose your data through GraphQL - read the ``Installation``, ``Schema`` and ``Queries`` section.
For more advanced use, check out the Relay tutorial.
.. toctree:: .. toctree::
:maxdepth: 0 :maxdepth: 1
installation
tutorial-plain tutorial-plain
tutorial-relay tutorial-relay
schema
queries
mutations
filtering filtering
authorization authorization
debug debug
rest-framework rest-framework
form-mutations
introspection introspection
testing testing

69
docs/installation.rst Normal file
View File

@ -0,0 +1,69 @@
Installation
============
Graphene-Django takes a few seconds to install and set up.
Requirements
------------
Graphene-Django currently supports the following versions of Django:
* Django 2.X
Installation
------------
.. code:: bash
pip install graphene-django
**We strongly recommend pinning against a specific version of Graphene-Django because new versions could introduce breaking changes to your project.**
Add ``graphene_django`` to the ``INSTALLED_APPS`` in the ``settings.py`` file of your Django project:
.. code:: python
INSTALLED_APPS = [
...
'django.contrib.staticfiles', # Required for GraphiQL
'graphene_django'
]
We need to add a graphql URL to the ``urls.py`` of your Django project:
.. code:: python
from django.conf.urls import url
from graphene_django.views import GraphQLView
urlpatterns = [
# ...
url(r'^graphql$', GraphQLView.as_view(graphiql=True)),
]
(Change ``graphiql=True`` to ``graphiql=False`` if you do not want to use the GraphiQL API browser.)
Finally, define the schema location for Graphene in the ``settings.py`` file of your Django project:
.. code:: python
GRAPHENE = {
'SCHEMA': 'django_root.schema.schema'
}
Where ``path.schema.schema`` is the location of the ``Schema`` object in your Django project.
The most basic ``schema.py`` looks like this:
.. code:: python
import graphene
class Query(graphene.ObjectType):
pass
schema = graphene.Schema(query=Query)
To learn how to extend the schema object for your project, read the basic tutorial.

View File

@ -5,8 +5,8 @@ Relay uses `Babel Relay
Plugin <https://facebook.github.io/relay/docs/guides-babel-plugin.html>`__ Plugin <https://facebook.github.io/relay/docs/guides-babel-plugin.html>`__
that requires you to provide your GraphQL schema data. that requires you to provide your GraphQL schema data.
Graphene comes with a management command for Django to dump your schema Graphene comes with a Django management command to dump your schema
data to ``schema.json`` that is compatible with babel-relay-plugin. data to ``schema.json`` which is compatible with babel-relay-plugin.
Usage Usage
----- -----

229
docs/mutations.rst Normal file
View File

@ -0,0 +1,229 @@
Mutations
=========
Introduction
------------
Graphene-Django makes it easy to perform mutations.
With Graphene-Django we can take advantage of pre-existing Django features to
quickly build CRUD functionality, while still using the core `graphene mutation <https://docs.graphene-python.org/en/latest/types/mutations/>`__
features to add custom mutations to a Django project.
Simple example
--------------
.. code:: python
import graphene
from graphene_django import DjangoObjectType
from .models import Question
class QuestionType(DjangoObjectType):
class Meta:
model = Question
class QuestionMutation(graphene.Mutation):
class Arguments:
# The input arguments for this mutation
text = graphene.String(required=True)
id = graphene.ID()
# The class attributes define the response of the mutation
question = graphene.Field(QuestionType)
def mutate(self, info, text, id):
question = Question.objects.get(pk=id)
question.text = text
question.save()
# Notice we return an instance of this mutation
return QuestionMutation(question=question)
class Mutation:
update_question = QuestionMutation.Field()
Django Forms
------------
Graphene-Django comes with mutation classes that will convert the fields on Django forms into inputs on a mutation.
DjangoFormMutation
~~~~~~~~~~~~~~~~~~
.. code:: python
from graphene_django.forms.mutation import DjangoFormMutation
class MyForm(forms.Form):
name = forms.CharField()
class MyMutation(DjangoFormMutation):
class Meta:
form_class = MyForm
``MyMutation`` will automatically receive an ``input`` argument. This argument should be a ``dict`` where the key is ``name`` and the value is a string.
DjangoModelFormMutation
~~~~~~~~~~~~~~~~~~~~~~~
``DjangoModelFormMutation`` will pull the fields from a ``ModelForm``.
.. code:: python
from graphene_django.forms.mutation import DjangoModelFormMutation
class Pet(models.Model):
name = models.CharField()
class PetForm(forms.ModelForm):
class Meta:
model = Pet
fields = ('name',)
# This will get returned when the mutation completes successfully
class PetType(DjangoObjectType):
class Meta:
model = Pet
class PetMutation(DjangoModelFormMutation):
pet = Field(PetType)
class Meta:
form_class = PetForm
``PetMutation`` will grab the fields from ``PetForm`` and turn them into inputs. If the form is valid then the mutation
will lookup the ``DjangoObjectType`` for the ``Pet`` model and return that under the key ``pet``. Otherwise it will
return a list of errors.
You can change the input name (default is ``input``) and the return field name (default is the model name lowercase).
.. code:: python
class PetMutation(DjangoModelFormMutation):
class Meta:
form_class = PetForm
input_field_name = 'data'
return_field_name = 'my_pet'
Form validation
~~~~~~~~~~~~~~~
Form mutations will call ``is_valid()`` on your forms.
If the form is valid then the class method ``perform_mutate(form, info)`` is called on the mutation. Override this method
to change how the form is saved or to return a different Graphene object type.
If the form is *not* valid then a list of errors will be returned. These errors have two fields: ``field``, a string
containing the name of the invalid form field, and ``messages``, a list of strings with the validation messages.
Django REST Framework
---------------------
You can re-use your Django Rest Framework serializer with Graphene Django mutations.
You can create a Mutation based on a serializer by using the `SerializerMutation` base class:
.. code:: python
from graphene_django.rest_framework.mutation import SerializerMutation
class MyAwesomeMutation(SerializerMutation):
class Meta:
serializer_class = MySerializer
Create/Update Operations
~~~~~~~~~~~~~~~~~~~~~~~~
By default ModelSerializers accept create and update operations. To
customize this use the `model_operations` attribute on the ``SerializerMutation`` class.
The update operation looks up models by the primary key by default. You can
customize the look up with the ``lookup_field`` attribute on the ``SerializerMutation`` class.
.. code:: python
from graphene_django.rest_framework.mutation import SerializerMutation
from .serializers imoprt MyModelSerializer
class AwesomeModelMutation(SerializerMutation):
class Meta:
serializer_class = MyModelSerializer
model_operations = ['create', 'update']
lookup_field = 'id'
Overriding Update Queries
~~~~~~~~~~~~~~~~~~~~~~~~~
Use the method ``get_serializer_kwargs`` to override how updates are applied.
.. code:: python
from graphene_django.rest_framework.mutation import SerializerMutation
from .serializers imoprt MyModelSerializer
class AwesomeModelMutation(SerializerMutation):
class Meta:
serializer_class = MyModelSerializer
@classmethod
def get_serializer_kwargs(cls, root, info, **input):
if 'id' in input:
instance = Post.objects.filter(
id=input['id'], owner=info.context.user
).first()
if instance:
return {'instance': instance, 'data': input, 'partial': True}
else:
raise http.Http404
return {'data': input, 'partial': True}
Relay
-----
You can use relay with mutations. A Relay mutation must inherit from
``ClientIDMutation`` and implement the ``mutate_and_get_payload`` method:
.. code:: python
import graphene import relay, DjangoObjectType
from graphql_relay import from_global_id
from .queries import QuestionType
class QuestionMutation(relay.ClientIDMutation):
class Input:
text = graphene.String(required=True)
id = graphene.ID()
question = graphene.Field(QuestionType)
@classmethod
def mutate_and_get_payload(cls, root, info, text, id):
question = Question.objects.get(pk=from_global_id(id))
question.text = text
question.save()
return QuestionMutation(question=question)
Notice that the ``class Arguments`` is renamed to ``class Input`` with relay.
This is due to a deprecation of ``class Arguments`` in graphene 2.0.
Relay ClientIDMutation accept a ``clientIDMutation`` argument.
This argument is also sent back to the client with the mutation result
(you do not have to do anything). For services that manage
a pool of many GraphQL requests in bulk, the ``clientIDMutation``
allows you to match up a specific mutation with the response.

270
docs/queries.rst Normal file
View File

@ -0,0 +1,270 @@
Queries & ObjectTypes
=====================
Introduction
------------
Graphene-Django offers a host of features for performing GraphQL queries.
Graphene-Django ships with a special ``DjangoObjectType`` that automatically transforms a Django Model
into a ``ObjectType`` for you.
Full example
~~~~~~~~~~~~
.. code:: python
# my_app/schema.py
import graphene
from graphene_django.types import DjangoObjectType
from .models import Question
class QuestionType(DjangoObjectType):
class Meta:
model = Question
class Query:
questions = graphene.List(QuestionType)
question = graphene.Field(Question, question_id=graphene.String())
def resolve_questions(self, info, **kwargs):
# Querying a list
return Question.objects.all()
def resolve_question(self, info, question_id):
# Querying a single question
return Question.objects.get(pk=question_id)
Fields
------
By default, ``DjangoObjectType`` will present all fields on a Model through GraphQL.
If you don't want to do this you can change this by setting either ``only_fields`` and ``exclude_fields``.
only_fields
~~~~~~~~~~~
Show **only** these fields on the model:
.. code:: python
class QuestionType(DjangoObjectType):
class Meta:
model = Question
only_fields = ('question_text')
exclude_fields
~~~~~~~~~~~~~~
Show all fields **except** those in ``exclude_fields``:
.. code:: python
class QuestionType(DjangoObjectType):
class Meta:
model = Question
exclude_fields = ('question_text')
Customised fields
~~~~~~~~~~~~~~~~~
You can completely overwrite a field, or add new fields, to a ``DjangoObjectType`` using a Resolver:
.. code:: python
class QuestionType(DjangoObjectType):
class Meta:
model = Question
exclude_fields = ('question_text')
extra_field = graphene.String()
def resolve_extra_field(self, info):
return 'hello!'
Related models
--------------
Say you have the following models:
.. code:: python
class Category(models.Model):
foo = models.CharField(max_length=256)
class Question(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
When ``Question`` is published as a ``DjangoObjectType`` and you want to add ``Category`` as a query-able field like so:
.. code:: python
class QuestionType(DjangoObjectType):
class Meta:
model = Question
only_fields = ('category',)
Then all query-able related models must be defined as DjangoObjectType subclass,
or they will fail to show if you are trying to query those relation fields. You only
need to create the most basic class for this to work:
.. code:: python
class CategoryType(DjangoObjectType):
class Meta:
model = Category
Default QuerySet
-----------------
If you are using ``DjangoObjectType`` you can define a custom `get_queryset` method.
Use this to control filtering on the ObjectType level instead of the Query object level.
.. code:: python
from graphene_django.types import DjangoObjectType
from .models import Question
class QuestionType(DjangoObjectType):
class Meta:
model = Question
@classmethod
def get_queryset(cls, queryset, info):
if info.context.user.is_anonymous:
return queryset.filter(published=True)
return queryset
Resolvers
---------
When a GraphQL query is received by the ``Schema`` object, it will map it to a "Resolver" related to it.
This resolve method should follow this format:
.. code:: python
def resolve_foo(self, info, **kwargs):
Where "foo" is the name of the field declared in the ``Query`` object.
.. code:: python
class Query:
foo = graphene.List(QuestionType)
def resolve_foo(self, info, **kwargs):
id = kwargs.get('id')
return QuestionModel.objects.get(id)
Arguments
~~~~~~~~~
Additionally, Resolvers will receive **any arguments declared in the field definition**. This allows you to provide input arguments in your GraphQL server and can be useful for custom queries.
.. code:: python
class Query:
question = graphene.Field(Question, foo=graphene.String(), bar=graphene.Int())
def resolve_question(self, info, foo, bar):
# If `foo` or `bar` are declared in the GraphQL query they will be here, else None.
return Question.objects.filter(foo=foo, bar=bar).first()
Info
~~~~
The ``info`` argument passed to all resolve methods holds some useful information.
For Graphene-Django, the ``info.context`` attribute is the ``HTTPRequest`` object
that would be familiar to any Django developer. This gives you the full functionality
of Django's ``HTTPRequest`` in your resolve methods, such as checking for authenticated users:
.. code:: python
def resolve_questions(self, info, **kwargs):
# See if a user is authenticated
if info.context.user.is_authenticated():
return Question.objects.all()
else:
return Question.objects.none()
Plain ObjectTypes
-----------------
With Graphene-Django you are not limited to just Django Models - you can use the standard
``ObjectType`` to create custom fields or to provide an abstraction between your internal
Django models and your external API.
.. code:: python
import graphene
from .models import Question
class MyQuestion(graphene.ObjectType):
text = graphene.String()
class Query:
question = graphene.Field(MyQuestion, question_id=graphene.String())
def resolve_question(self, info, question_id):
question = Question.objects.get(pk=question_id)
return MyQuestion(
text=question.question_text
)
For more information and more examples, please see the `core object type documentation <https://docs.graphene-python.org/en/latest/types/objecttypes/>`__.
Relay
-----
`Relay <http://docs.graphene-python.org/en/latest/relay/>`__ with Graphene-Django gives us some additional features:
- Pagination and slicing.
- An abstract ``id`` value which contains enough info for the server to know its type and its id.
There is one additional import and a single line of code needed to adopt this:
Full example
~~~~~~~~~~~~
.. code:: python
from graphene import relay
from graphene_django import DjangoObjectType
from .models import Question
class QuestionType(DjangoObjectType):
class Meta:
model = Question
interaces = (relay.Node,)
class QuestionConnection(relay.Connection):
class Meta:
node = QuestionType
class Query:
question = graphene.Field(QuestionType)
questions = relay.ConnectionField(QuestionConnection)
See the `Relay documentation <https://docs.graphene-python.org/en/latest/relay/nodes/>`__ on
the core graphene pages for more information on customing the Relay experience.

View File

@ -1,64 +0,0 @@
Integration with Django Rest Framework
======================================
You can re-use your Django Rest Framework serializer with
graphene django.
Mutation
--------
You can create a Mutation based on a serializer by using the
`SerializerMutation` base class:
.. code:: python
from graphene_django.rest_framework.mutation import SerializerMutation
class MyAwesomeMutation(SerializerMutation):
class Meta:
serializer_class = MySerializer
Create/Update Operations
---------------------
By default ModelSerializers accept create and update operations. To
customize this use the `model_operations` attribute. The update
operation looks up models by the primary key by default. You can
customize the look up with the lookup attribute.
.. code:: python
from graphene_django.rest_framework.mutation import SerializerMutation
class AwesomeModelMutation(SerializerMutation):
class Meta:
serializer_class = MyModelSerializer
model_operations = ['create', 'update']
lookup_field = 'id'
Overriding Update Queries
-------------------------
Use the method `get_serializer_kwargs` to override how
updates are applied.
.. code:: python
from graphene_django.rest_framework.mutation import SerializerMutation
class AwesomeModelMutation(SerializerMutation):
class Meta:
serializer_class = MyModelSerializer
@classmethod
def get_serializer_kwargs(cls, root, info, **input):
if 'id' in input:
instance = Post.objects.filter(id=input['id'], owner=info.context.user).first()
if instance:
return {'instance': instance, 'data': input, 'partial': True}
else:
raise http.Http404
return {'data': input, 'partial': True}

50
docs/schema.rst Normal file
View File

@ -0,0 +1,50 @@
Schema
======
The ``graphene.Schema`` object describes your data model and provides a GraphQL server with an associated set of resolve methods that know how to fetch data. The most basic schema you can create looks like this:
.. code:: python
import graphene
class Query(graphene.ObjectType):
pass
class Mutation(graphene.ObjectType):
pass
schema = graphene.Schema(query=Query, mutation=Mutation)
This schema doesn't do anything yet, but it is ready to accept new Query or Mutation fields.
Adding to the schema
--------------------
If you have defined a ``Query`` or ``Mutation``, you can register them with the schema:
.. code:: python
import graphene
import my_app.schema.Query
import my_app.schema.Mutation
class Query(
my_app.schema.Query, # Add your Query objects here
graphene.ObjectType
):
pass
class Mutation(
my_app.schema.Mutation, # Add your Mutation objects here
graphene.ObjectType
):
pass
schema = graphene.Schema(query=Query, mutation=Mutation)
You can add as many mixins to the base ``Query`` and ``Mutation`` objects as you like.
Read more about Schema on the `core graphene docs <https://docs.graphene-python.org/en/latest/types/schema/>`__

View File

@ -1,12 +1,9 @@
Introduction tutorial - Graphene and Django Basic Tutorial
=========================================== ===========================================
Graphene has a number of additional features that are designed to make Graphene Django has a number of additional features that are designed to make
working with Django *really simple*. working with Django easy. Our primary focus in this tutorial is to give a good
understanding of how to connect models from Django ORM to graphene object types.
Our primary focus here is to give a good understanding of how to connect models from Django ORM to graphene object types.
A good idea is to check the `graphene <http://docs.graphene-python.org/en/latest/>`__ documentation first.
Set up the Django project Set up the Django project
------------------------- -------------------------
@ -91,7 +88,7 @@ Don't forget to create & run migrations:
python manage.py makemigrations python manage.py makemigrations
python manage.py migrate python manage.py migrate
Load some test data Load some test data
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
@ -108,7 +105,7 @@ following:
$ python ./manage.py loaddata ingredients $ python ./manage.py loaddata ingredients
Installed 6 object(s) from 1 fixture(s) Installed 6 object(s) from 1 fixture(s)
Alternatively you can use the Django admin interface to create some data Alternatively you can use the Django admin interface to create some data
yourself. You'll need to run the development server (see below), and yourself. You'll need to run the development server (see below), and
create a login for yourself too (``./manage.py createsuperuser``). create a login for yourself too (``./manage.py createsuperuser``).
@ -255,7 +252,7 @@ aforementioned GraphiQL we specify that on the parameters with ``graphiql=True``
urlpatterns = [ urlpatterns = [
url(r'^admin/', admin.site.urls), url(r'^admin/', admin.site.urls),
url(r'^graphql', GraphQLView.as_view(graphiql=True)), url(r'^graphql$', GraphQLView.as_view(graphiql=True)),
] ]
@ -273,7 +270,7 @@ as explained above, we can do so here using:
urlpatterns = [ urlpatterns = [
url(r'^admin/', admin.site.urls), url(r'^admin/', admin.site.urls),
url(r'^graphql', GraphQLView.as_view(graphiql=True, schema=schema)), url(r'^graphql$', GraphQLView.as_view(graphiql=True, schema=schema)),
] ]
@ -487,7 +484,7 @@ Now, with the code in place, we can query for single objects.
For example, lets query ``category``: For example, lets query ``category``:
.. code:: .. code::
query { query {
category(id: 1) { category(id: 1) {
@ -536,3 +533,6 @@ Summary
As you can see, GraphQL is very powerful but there are a lot of repetitions in our example. We can do a lot of improvements by adding layers of abstraction on top of ``graphene-django``. As you can see, GraphQL is very powerful but there are a lot of repetitions in our example. We can do a lot of improvements by adding layers of abstraction on top of ``graphene-django``.
If you want to put things like ``django-filter`` and automatic pagination in action, you should continue with the **relay tutorial.** If you want to put things like ``django-filter`` and automatic pagination in action, you should continue with the **relay tutorial.**
A good idea is to check the `graphene <http://docs.graphene-python.org/en/latest/>`__
documentation but it is not essential to understand and use Graphene-Django in your project.

View File

@ -1,4 +1,4 @@
Graphene and Django Tutorial using Relay Relay tutorial
======================================== ========================================
Graphene has a number of additional features that are designed to make Graphene has a number of additional features that are designed to make
@ -244,7 +244,7 @@ aforementioned GraphiQL we specify that on the params with ``graphiql=True``.
urlpatterns = [ urlpatterns = [
url(r'^admin/', admin.site.urls), url(r'^admin/', admin.site.urls),
url(r'^graphql', GraphQLView.as_view(graphiql=True)), url(r'^graphql$', GraphQLView.as_view(graphiql=True)),
] ]
@ -262,7 +262,7 @@ as explained above, we can do so here using:
urlpatterns = [ urlpatterns = [
url(r'^admin/', admin.site.urls), url(r'^admin/', admin.site.urls),
url(r'^graphql', GraphQLView.as_view(graphiql=True, schema=schema)), url(r'^graphql$', GraphQLView.as_view(graphiql=True, schema=schema)),
] ]

View File

@ -6,5 +6,5 @@ from graphene_django.views import GraphQLView
urlpatterns = [ urlpatterns = [
url(r'^admin/', admin.site.urls), url(r'^admin/', admin.site.urls),
url(r'^graphql', GraphQLView.as_view(graphiql=True)), url(r'^graphql$', GraphQLView.as_view(graphiql=True)),
] ]