From 5e6f4cf302ea1e8615db1b1eb7c0e6c886ef0393 Mon Sep 17 00:00:00 2001 From: Adam Charnock Date: Tue, 29 Dec 2015 14:18:32 +0000 Subject: [PATCH] Further work on django quickstart --- docs/pages/docs/quickstart-django.md | 162 ++++++++++++++++++++++----- 1 file changed, 132 insertions(+), 30 deletions(-) diff --git a/docs/pages/docs/quickstart-django.md b/docs/pages/docs/quickstart-django.md index 9d0c816d..b944ef4c 100644 --- a/docs/pages/docs/quickstart-django.md +++ b/docs/pages/docs/quickstart-django.md @@ -30,10 +30,17 @@ from django.db import models class Category(models.Model): name = models.CharField(max_length=100) + def __str__(self): + return self.name + class Ingredient(models.Model): name = models.CharField(max_length=100) - category = models.ForeignKey(Category) + notes = models.TextField() + category = models.ForeignKey(Category, related_name='ingredients') + + def __str__(self): + return self.name ``` ## Schema @@ -51,84 +58,179 @@ Create `cookbook/ingredients/schema.py` and type the following: ```python # cookbook/ingredients/schema.py -import graphene -from graphene.contrib.django import DjangoObjectType +from graphene import relay, ObjectType +from graphene.contrib.django.filter import DjangoFilterConnectionField +from graphene.contrib.django.types import DjangoNode + +from cookbook.ingredients.models import Category, Ingredient -from django.contrib.auth.models import User, Group # Graphene will automatically map the User model's fields onto the UserType. # This is configured in the UserType's Meta class (as you can see below) -class UserType(DjangoObjectType): +class CategoryNode(DjangoNode): class Meta: - model = User - only_fields = ('username', 'email', 'groups') + model = Category + filter_fields = ['name', 'ingredients'] -class GroupType(DjangoObjectType): +class IngredientNode(DjangoNode): class Meta: - model = Group - only_fields = ('name', ) + model = Ingredient + filter_fields = ['name', 'notes', 'category'] -class Query(graphene.ObjectType): - get_user = graphene.Field(UserType, - id=graphene.String().NonNull) - get_group = graphene.Field(GroupType, - id=graphene.String().NonNull) +class Query(ObjectType): + category = relay.NodeField(CategoryNode) + all_categories = DjangoFilterConnectionField(CategoryNode) -schema = graphene.Schema(query=Query) + ingredient = relay.NodeField(IngredientNode) + all_ingredients = DjangoFilterConnectionField(IngredientNode) + + class Meta: + abstract = True ``` +Note that the above `Query` class is marked as 'abstract'. This is because we +want will now create a project-level query which will combine all our app-level +queries. + +Create the parent project-level `cookbook/schema.py`: + +```python +import graphene + +import cookbook.ingredients.schema + + +class Query(cookbook.ingredients.schema.Query): + # This class will inherit from multiple Queries + # as we begin to add more apps to our project + pass + +schema = graphene.Schema(name='Cookbook Schema') +schema.query = Query +``` + +You can think of this as being something like your top-level `urls.py` +file (although it currently lacks any namespacing). ## Adding GraphiQL -For having the GraphiQL static assets we need to append `django_graphiql` in `INSTALLED_APPS` in `tutorial/settings.py`: +GraphiQL is a web-based integrated development environment to assist in the +writing and executing of GraphQL queries. It will provide us with a simple +and easy way of testing our cookbook project. + +Add `django_graphiql` to `INSTALLED_APPS` in `cookbook/settings.py`: ```python INSTALLED_APPS = [ - # The other installed apps + ... 'django_graphiql', ] ``` ## Creating GraphQL and GraphiQL views -Unlike a RESTful API, there is only a single URL from which a GraphQL is accessed. +Unlike a RESTful API, there is only a single URL from which GraphQL is accessed. Requests to this URL are handled by Graphene's `GraphQLView` view. -Additionally, an interface for navigating this API will be very useful. Graphene -includes the [graphiql](https://github.com/graphql/graphiql) in-browser IDE -which assists in exploring and querying your new API. We’ll add a URL for this too. +Additionally, we'll add a URL for aforementioned GraphiQL, and for the Django admin +interface (the latter can be useful for creating test data). ```python from django.conf.urls import url, include +from django.contrib import admin from django.views.decorators.csrf import csrf_exempt + from graphene.contrib.django.views import GraphQLView -from quickstart.schema import schema +from cookbook.schema import schema -# Wire up our GraphQL schema to /graphql. -# Additionally, we include GraphiQL view for querying easily our schema. urlpatterns = [ + url(r'^admin/', admin.site.urls), url(r'^graphql', csrf_exempt(GraphQLView.as_view(schema=schema))), url(r'^graphiql', include('django_graphiql.urls')), ] ``` +## Load some test data + +**TODO:** Insert link to fixture + +Now is a good time to load up some test data. The easiest option will be to download +the ingredients.json fixture and place it in +`cookbook/ingredients/fixtures/ingredients.json`. You can then run the 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 youself. +You'll need to run the development server (see below), and probably create a login +for yourself too (`./manage.py createsuperuser`). + ## Testing our GraphQL schema We're now ready to test the API we've built. Let's fire up the server from the command line. ```bash -python ./manage.py runserver +$ python ./manage.py runserver + +Performing system checks... +Django version 1.9, using settings 'cookbook.settings' +Starting development server at http://127.0.0.1:8000/ +Quit the server with CONTROL-C. ``` -Go to [localhost:8080/graphiql](http://localhost:8080/graphiql) and type your first query! +Go to [localhost:8000/graphiql](http://localhost:8000/graphiql) and type your first query! ```graphql -myQuery { - getUser(id:"1") { - username +query { + allIngredients { + edges { + node { + id, + name + } } + } +} +``` + +The above will return the names & IDs for all ingredients. But perhaps you want +a specific ingredient: + +```graphql +query { + # Graphene creates globally unique IDs for all objects. + # You may need to copy this value from the results of the first query + ingredient(id: "SW5ncmVkaWVudE5vZGU6MQ==") { + name + } +} +``` + +You can also get each ingredient for each category: + +```graphql +query { + allCategories { + edges { + node { + name, + + ingredients { + edges { + node { + name + } + } + } + + } + } + } } ```