mirror of
https://github.com/graphql-python/graphene-django.git
synced 2024-11-10 19:57:15 +03:00
Rebuild documentation
This commit is contained in:
parent
6f03597a5e
commit
31468f5687
|
@ -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)),
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
12
README.rst
12
README.rst
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
89
docs/conf.py
89
docs/conf.py
|
@ -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}
|
||||||
|
|
|
@ -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',
|
||||||
]
|
]
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
|
|
|
@ -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
69
docs/installation.rst
Normal 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.
|
|
@ -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
229
docs/mutations.rst
Normal 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
270
docs/queries.rst
Normal 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.
|
|
@ -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
50
docs/schema.rst
Normal 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/>`__
|
|
@ -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.
|
|
@ -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)),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)),
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user