diff --git a/README.md b/README.md index d2fe4b6c4..159a5924d 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 2e0593dfc..44feaee83 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 3b343264d..3d0bb8adf 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/filtering.rst b/docs/filtering.rst index feafd4041..d02366f20 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 85b89e8ff..000000000 --- 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 9469c29ca..c7820cfe0 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 000000000..8f3e550cc --- /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/mutations.rst b/docs/mutations.rst new file mode 100644 index 000000000..6e0910334 --- /dev/null +++ b/docs/mutations.rst @@ -0,0 +1,219 @@ +Mutations +========= + +Introduction +------------ + +Graphene-Django makes it easy to perform mutations too. + +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 Input: + # 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) + + def mutate_and_get_payload(self info, text, id): + question = Question.objects.get(pk=from_global_id(id)) + question.text = text + question.save() + return QuestionMutation(question=question) \ No newline at end of file diff --git a/docs/queries.rst b/docs/queries.rst new file mode 100644 index 000000000..3e41fa68b --- /dev/null +++ b/docs/queries.rst @@ -0,0 +1,237 @@ +Queries & ObjectTypes +===================== + +Introduction +------------ + +Graphene-Django offers a host of features when 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!' + + +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 ce666dea8..000000000 --- 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 000000000..9f0c283a1 --- /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 a87b011cb..98771e3c9 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``). @@ -171,10 +168,10 @@ Create ``cookbook/ingredients/schema.py`` and type the following: all_categories = graphene.List(CategoryType) all_ingredients = graphene.List(IngredientType) - def resolve_all_categories(self, info, **kwargs): + def resolve_all_categories(self, context, **kwargs): return Category.objects.all() - def resolve_all_ingredients(self, info, **kwargs): + def resolve_all_ingredients(self, context, **kwargs): # We can easily optimize query count in the resolve method return Ingredient.objects.select_related('category').all() @@ -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)), ] @@ -452,13 +449,13 @@ We can update our schema to support that, by adding new query for ``ingredient`` name=graphene.String()) all_ingredients = graphene.List(IngredientType) - def resolve_all_categories(self, info, **kwargs): + def resolve_all_categories(self, context, **kwargs): return Category.objects.all() - def resolve_all_ingredients(self, info, **kwargs): + def resolve_all_ingredients(self, context, **kwargs): return Ingredient.objects.all() - def resolve_category(self, info, **kwargs): + def resolve_category(self, context, **kwargs): id = kwargs.get('id') name = kwargs.get('name') @@ -470,7 +467,7 @@ We can update our schema to support that, by adding new query for ``ingredient`` return None - def resolve_ingredient(self, info, **kwargs): + def resolve_ingredient(self, context, **kwargs): id = kwargs.get('id') name = kwargs.get('name') @@ -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 630898e1c..5f8bd649f 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/cookbook/urls.py b/examples/cookbook/cookbook/urls.py index 9f8755b7a..4bf60032e 100644 --- a/examples/cookbook/cookbook/urls.py +++ b/examples/cookbook/cookbook/urls.py @@ -6,5 +6,5 @@ urlpatterns = [ url(r'^admin/', admin.site.urls), - url(r'^graphql', GraphQLView.as_view(graphiql=True)), + url(r'^graphql$', GraphQLView.as_view(graphiql=True)), ]