diff --git a/README.md b/README.md
index d2fe4b6..159a592 100644
--- a/README.md
+++ b/README.md
@@ -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/).
-## 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
@@ -39,7 +43,7 @@ from graphene_django.views import GraphQLView
urlpatterns = [
# ...
- url(r'^graphql', GraphQLView.as_view(graphiql=True)),
+ url(r'^graphql$', GraphQLView.as_view(graphiql=True)),
]
```
diff --git a/README.rst b/README.rst
index 2e0593d..44feaee 100644
--- a/README.rst
+++ b/README.rst
@@ -10,8 +10,14 @@ to learn how to upgrade to Graphene ``2.0``.
A `Django `__ integration for
`Graphene `__.
-Installation
-------------
+
+Documentation
+-------------
+
+`Visit the documentation to get started! `__
+
+Quickstart
+----------
For installing graphene, just run this command in your shell
@@ -46,7 +52,7 @@ serve the queries.
urlpatterns = [
# ...
- url(r'^graphql', GraphQLView.as_view(graphiql=True)),
+ url(r'^graphql$', GraphQLView.as_view(graphiql=True)),
]
Examples
diff --git a/docs/authorization.rst b/docs/authorization.rst
index 3b34326..3d0bb8a 100644
--- a/docs/authorization.rst
+++ b/docs/authorization.rst
@@ -155,7 +155,7 @@ To restrict users from accessing the GraphQL API page the standard Django LoginR
.. code:: python
#views.py
-
+
from django.contrib.auth.mixins import LoginRequiredMixin
from graphene_django.views import GraphQLView
@@ -171,9 +171,9 @@ For Django 1.9 and below:
urlpatterns = [
# 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:
.. code:: python
diff --git a/docs/conf.py b/docs/conf.py
index 2ea2d55..a485d5b 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,6 +1,6 @@
import os
-on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
+on_rtd = os.environ.get("READTHEDOCS", None) == "True"
# -*- 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
# ones.
extensions = [
- 'sphinx.ext.autodoc',
- 'sphinx.ext.intersphinx',
- 'sphinx.ext.todo',
- 'sphinx.ext.coverage',
- 'sphinx.ext.viewcode',
+ "sphinx.ext.autodoc",
+ "sphinx.ext.intersphinx",
+ "sphinx.ext.todo",
+ "sphinx.ext.coverage",
+ "sphinx.ext.viewcode",
]
if not on_rtd:
- extensions += [
- 'sphinx.ext.githubpages',
- ]
+ extensions += ["sphinx.ext.githubpages"]
# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
-source_suffix = '.rst'
+source_suffix = ".rst"
# The encoding of source files.
#
# source_encoding = 'utf-8-sig'
# The master toctree document.
-master_doc = 'index'
+master_doc = "index"
# General information about the project.
-project = u'Graphene Django'
-copyright = u'Graphene 2017'
-author = u'Syrus Akbary'
+project = u"Graphene Django"
+copyright = u"Graphene 2017"
+author = u"Syrus Akbary"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
-version = u'1.0'
+version = u"1.0"
# 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
# for a list of supported languages.
@@ -94,7 +92,7 @@ language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# 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
# documents.
@@ -116,7 +114,7 @@ exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# show_authors = False
# 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.
# 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,
# relative to this directory. They are copied after the builtin static files,
# 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
# .htaccess) here, relative to this directory. These files are copied
@@ -255,34 +253,30 @@ html_static_path = ['_static']
# html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder.
-htmlhelp_basename = 'Graphenedoc'
+htmlhelp_basename = "Graphenedoc"
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
- # The paper size ('letterpaper' or 'a4paper').
- #
- # 'papersize': 'letterpaper',
-
- # The font size ('10pt', '11pt' or '12pt').
- #
- # 'pointsize': '10pt',
-
- # Additional stuff for the LaTeX preamble.
- #
- # 'preamble': '',
-
- # Latex figure (float) alignment
- #
- # 'figure_align': 'htbp',
+ # The paper size ('letterpaper' or 'a4paper').
+ #
+ # 'papersize': 'letterpaper',
+ # The font size ('10pt', '11pt' or '12pt').
+ #
+ # 'pointsize': '10pt',
+ # Additional stuff for the LaTeX preamble.
+ #
+ # 'preamble': '',
+ # Latex figure (float) alignment
+ #
+ # 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
- (master_doc, 'Graphene.tex', u'Graphene Documentation',
- u'Syrus Akbary', 'manual'),
+ (master_doc, "Graphene.tex", u"Graphene Documentation", u"Syrus Akbary", "manual")
]
# 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
# (source start file, name, description, authors, manual section).
man_pages = [
- (master_doc, 'graphene_django', u'Graphene Django Documentation',
- [author], 1)
+ (master_doc, "graphene_django", u"Graphene Django Documentation", [author], 1)
]
# If true, show URL addresses after external links.
@@ -338,9 +331,15 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
- (master_doc, 'Graphene-Django', u'Graphene Django Documentation',
- author, 'Graphene Django', 'One line description of project.',
- 'Miscellaneous'),
+ (
+ master_doc,
+ "Graphene-Django",
+ u"Graphene Django Documentation",
+ author,
+ "Graphene Django",
+ "One line description of project.",
+ "Miscellaneous",
+ )
]
# Documents to append as an appendix to all manuals.
@@ -414,7 +413,7 @@ epub_copyright = copyright
# epub_post_files = []
# 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.
#
@@ -446,4 +445,4 @@ epub_exclude_files = ['search.html']
# Example configuration for intersphinx: refer to the Python standard library.
-intersphinx_mapping = {'https://docs.python.org/': None}
+intersphinx_mapping = {"https://docs.python.org/": None}
diff --git a/docs/debug.rst b/docs/debug.rst
index 8ef2e86..8e67c23 100644
--- a/docs/debug.rst
+++ b/docs/debug.rst
@@ -34,6 +34,7 @@ And in your ``settings.py``:
.. code:: python
GRAPHENE = {
+ ...
'MIDDLEWARE': [
'graphene_django.debug.DjangoDebugMiddleware',
]
diff --git a/docs/filtering.rst b/docs/filtering.rst
index feafd40..d02366f 100644
--- a/docs/filtering.rst
+++ b/docs/filtering.rst
@@ -136,7 +136,7 @@ pre-filter animals owned by the authenticated user (set in ``context.user``).
class AnimalFilter(django_filters.FilterSet):
# Do case-insensitive lookups on 'name'
- name = django_filters.CharFilter(lookup_type='iexact')
+ name = django_filters.CharFilter(lookup_type=['iexact'])
class Meta:
model = Animal
@@ -146,3 +146,49 @@ pre-filter animals owned by the authenticated user (set in ``context.user``).
def qs(self):
# The query context can be found in self.request.
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
+ }
+ }
+ }
\ No newline at end of file
diff --git a/docs/form-mutations.rst b/docs/form-mutations.rst
deleted file mode 100644
index 85b89e8..0000000
--- a/docs/form-mutations.rst
+++ /dev/null
@@ -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.
diff --git a/docs/index.rst b/docs/index.rst
index 9469c29..c7820cf 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,17 +1,34 @@
Graphene-Django
===============
-Contents:
+Welcome to the Graphene-Django docs.
+
+Graphene-Django is built on top of `Graphene `__.
+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 `__ 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::
- :maxdepth: 0
+ :maxdepth: 1
+ installation
tutorial-plain
tutorial-relay
+ schema
+ queries
+ mutations
filtering
authorization
debug
rest-framework
- form-mutations
introspection
testing
diff --git a/docs/installation.rst b/docs/installation.rst
new file mode 100644
index 0000000..8f3e550
--- /dev/null
+++ b/docs/installation.rst
@@ -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.
\ No newline at end of file
diff --git a/docs/introspection.rst b/docs/introspection.rst
index bd80f26..92e3612 100644
--- a/docs/introspection.rst
+++ b/docs/introspection.rst
@@ -5,8 +5,8 @@ Relay uses `Babel Relay
Plugin `__
that requires you to provide your GraphQL schema data.
-Graphene comes with a management command for Django to dump your schema
-data to ``schema.json`` that is compatible with babel-relay-plugin.
+Graphene comes with a Django management command to dump your schema
+data to ``schema.json`` which is compatible with babel-relay-plugin.
Usage
-----
diff --git a/docs/mutations.rst b/docs/mutations.rst
new file mode 100644
index 0000000..f6c6f14
--- /dev/null
+++ b/docs/mutations.rst
@@ -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 `__
+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.
\ No newline at end of file
diff --git a/docs/queries.rst b/docs/queries.rst
new file mode 100644
index 0000000..d54c908
--- /dev/null
+++ b/docs/queries.rst
@@ -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 `__.
+
+
+Relay
+-----
+
+`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 `__ on
+the core graphene pages for more information on customing the Relay experience.
\ No newline at end of file
diff --git a/docs/rest-framework.rst b/docs/rest-framework.rst
deleted file mode 100644
index ce666de..0000000
--- a/docs/rest-framework.rst
+++ /dev/null
@@ -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}
diff --git a/docs/schema.rst b/docs/schema.rst
new file mode 100644
index 0000000..9f0c283
--- /dev/null
+++ b/docs/schema.rst
@@ -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 `__
\ No newline at end of file
diff --git a/docs/tutorial-plain.rst b/docs/tutorial-plain.rst
index a87b011..29df56e 100644
--- a/docs/tutorial-plain.rst
+++ b/docs/tutorial-plain.rst
@@ -1,12 +1,9 @@
-Introduction tutorial - Graphene and Django
+Basic Tutorial
===========================================
-Graphene has a number of additional features that are designed to make
-working with Django *really simple*.
-
-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 `__ documentation first.
+Graphene Django has a number of additional features that are designed to make
+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.
Set up the Django project
-------------------------
@@ -91,7 +88,7 @@ Don't forget to create & run migrations:
python manage.py makemigrations
python manage.py migrate
-
+
Load some test data
^^^^^^^^^^^^^^^^^^^
@@ -108,7 +105,7 @@ following:
$ python ./manage.py loaddata ingredients
Installed 6 object(s) from 1 fixture(s)
-
+
Alternatively you can use the Django admin interface to create some data
yourself. You'll need to run the development server (see below), and
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 = [
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 = [
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``:
-.. code::
+.. code::
query {
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``.
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 `__
+documentation but it is not essential to understand and use Graphene-Django in your project.
\ No newline at end of file
diff --git a/docs/tutorial-relay.rst b/docs/tutorial-relay.rst
index 630898e..5f8bd64 100644
--- a/docs/tutorial-relay.rst
+++ b/docs/tutorial-relay.rst
@@ -1,4 +1,4 @@
-Graphene and Django Tutorial using Relay
+Relay tutorial
========================================
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 = [
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 = [
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)),
]
diff --git a/examples/cookbook-plain/requirements.txt b/examples/cookbook-plain/requirements.txt
index 539fd67..2154fd8 100644
--- a/examples/cookbook-plain/requirements.txt
+++ b/examples/cookbook-plain/requirements.txt
@@ -1,4 +1,4 @@
graphene
graphene-django
graphql-core>=2.1rc1
-django==2.1.2
+django==2.1.6
diff --git a/examples/cookbook/cookbook/urls.py b/examples/cookbook/cookbook/urls.py
index 9f8755b..4bf6003 100644
--- a/examples/cookbook/cookbook/urls.py
+++ b/examples/cookbook/cookbook/urls.py
@@ -6,5 +6,5 @@ from graphene_django.views import GraphQLView
urlpatterns = [
url(r'^admin/', admin.site.urls),
- url(r'^graphql', GraphQLView.as_view(graphiql=True)),
+ url(r'^graphql$', GraphQLView.as_view(graphiql=True)),
]
diff --git a/examples/cookbook/requirements.txt b/examples/cookbook/requirements.txt
index b2ace1f..3fed30f1 100644
--- a/examples/cookbook/requirements.txt
+++ b/examples/cookbook/requirements.txt
@@ -1,5 +1,5 @@
graphene
graphene-django
graphql-core>=2.1rc1
-django==1.9
+django==1.11.19
django-filter>=2