From 216c4350a1cd7e110f54834d5e3d0c825f1640aa Mon Sep 17 00:00:00 2001
From: Adam Charnock <acharnock@twitter.com>
Date: Thu, 31 Dec 2015 02:58:13 +0000
Subject: [PATCH] Further additions to docs for new django functionality

---
 docs/config.toml                     |   1 +
 docs/pages/docs/filtering.md         | 147 +++++++++++++++++++++++++++
 docs/pages/docs/quickstart-django.md |  38 +++++--
 3 files changed, 176 insertions(+), 10 deletions(-)
 create mode 100644 docs/pages/docs/filtering.md

diff --git a/docs/config.toml b/docs/config.toml
index 569605d9..5e39bed6 100644
--- a/docs/config.toml
+++ b/docs/config.toml
@@ -16,4 +16,5 @@ ga = "UA-12613282-7"
 	  "/docs/mutations/",
 	  "/docs/basic-types/",
 	  "/docs/relay/",
+	  "/docs/filtering/",
 	]
diff --git a/docs/pages/docs/filtering.md b/docs/pages/docs/filtering.md
new file mode 100644
index 00000000..5ef57ea5
--- /dev/null
+++ b/docs/pages/docs/filtering.md
@@ -0,0 +1,147 @@
+---
+title: Filtering (Django)
+description: Details of how to perform filtering
+---
+
+# Filtering (Django)
+
+Graphene integrates with [django-filter](https://django-filter.readthedocs.org)
+to provide filtering of results. See the
+[usage documentation](https://django-filter.readthedocs.org/en/latest/usage.html#the-filter)
+for details on the format for `filter_fields`.
+
+**Note 1:** This filtering is only available when using the Django integrations
+(i.e. nodes which extend `DjangoNode`)
+
+**Note 2:** `django-filter` is an optional dependency of Graphene. You will need to
+install it manually, which can be done as follows:
+
+```bash
+pip install django-filter
+```
+
+## Filterable fields
+
+The `filter_fields` parameter is used to specify the fields which can be filtered upon.
+The value specified here is passed directly to `django-filter`, so see the
+[filtering documentation](https://django-filter.readthedocs.org/en/latest/usage.html#the-filter)
+for full details on the range of options available.
+
+For example:
+
+```python
+class AnimalNode(DjangoNode):
+    class Meta:
+        # Assume you have an Animal model defined with the following fields
+        model = Animal
+        filter_fields = ['name', 'genus', 'is_domesticated']
+
+class Query(ObjectType):
+    animal = relay.NodeField(AnimalNode)
+    all_animals = DjangoFilterConnectionField(AnimalNode)
+```
+
+You could then perform a query such as:
+
+```graphql
+query {
+  # Note that fields names become camelcased
+  allAnimals(genus: "cat", isDomesticated: true) {
+    edges {
+      node {
+        id,
+        name
+}}}}
+```
+
+You can also make more complex lookup types available:
+
+```python
+class AnimalNode(DjangoNode):
+    class Meta:
+        model = Animal
+        # Provide more complex lookup types
+        filter_fields = {
+            'name': ['exact', 'icontains', 'istartswith'],
+            'genus': ['exact'],
+            'is_domesticated': ['exact'],
+        }
+```
+
+Which you could query as follows:
+
+```graphql
+query {
+  # Note that fields names become camelcased
+  allAnimals(nameIcontains: "lion") {
+    edges {
+      node {
+        id,
+        name
+}}}}
+```
+
+## Orderable fields
+
+Ordering can also be specified using `filter_order_by`. Like `filter_fields`,
+this value is also passed directly to `django-filter` as the `order_by` field.
+For full details see the
+[order_by documentation](https://django-filter.readthedocs.org/en/latest/usage.html#ordering-using-order-by).
+
+For example:
+
+```python
+class AnimalNode(DjangoNode):
+    class Meta:
+        model = Animal
+        filter_fields = ['name', 'genus', 'is_domesticated']
+        # Either a tuple/list of fields upon which ordering is allowed, or
+        # True to allow filtering on all fields specified in filter_fields
+        order_by_fields = True
+```
+
+You can then control the ordering via the `orderBy` argument:
+
+```graphql
+query {
+  allAnimals(orderBy: "name") {
+    edges {
+      node {
+        id,
+        name
+}}}}
+```
+
+## Custom Filtersets
+
+By default Graphene provides easy access to the most commonly used
+features of `django-filter`. This is done by transparently creating a
+`django_filters.FilterSet` class for you and passing in the values for
+`filter_fields` and `order_by_fields`.
+
+However, you may find this to be insufficient. In these cases you can
+create your own `Filterset` as follows:
+
+```python
+class AnimalNode(DjangoNode):
+    class Meta:
+        # Assume you have an Animal model defined with the following fields
+        model = Animal
+        filter_fields = ['name', 'genus', 'is_domesticated']
+
+
+class AnimalFilter(django_filters.FilterSet):
+    # Do case-insensitive lookups on 'name'
+    name = django_filters.CharFilter(lookup_type='iexact')
+
+    class Meta:
+        model = Animal
+        fields = ['name', 'genus', 'is_domesticated']
+
+
+class Query(ObjectType):
+    animal = relay.NodeField(AnimalNode)
+    # We specify our custom AnimalFilter using the filterset_class param
+    all_animals = DjangoFilterConnectionField(AnimalNode,
+                                              filterset_class=AnimalFilter)
+```
diff --git a/docs/pages/docs/quickstart-django.md b/docs/pages/docs/quickstart-django.md
index b944ef4c..e2b94761 100644
--- a/docs/pages/docs/quickstart-django.md
+++ b/docs/pages/docs/quickstart-django.md
@@ -1,5 +1,5 @@
 ---
-title: Django Tutorial
+title: Django Quickstart
 description: A Quick guide to Graphene in Django
 ---
 
@@ -71,12 +71,20 @@ class CategoryNode(DjangoNode):
     class Meta:
         model = Category
         filter_fields = ['name', 'ingredients']
+        filter_order_by = ['name']
 
 
 class IngredientNode(DjangoNode):
     class Meta:
         model = Ingredient
-        filter_fields = ['name', 'notes', 'category']
+        # Allow for some more advanced filtering here
+        filter_fields = {
+            'name': ['exact', 'icontains', 'istartswith'],
+            'notes': ['exact', 'icontains'],
+            'category': ['exact'],
+            'category__name': ['exact'],
+        }
+        filter_order_by = ['name', 'category__name']
 
 
 class Query(ObjectType):
@@ -90,6 +98,11 @@ class Query(ObjectType):
         abstract = True
 ```
 
+The filtering functionality is provided by
+[django-filter](https://django-filter.readthedocs.org). See the
+[usage documentation](https://django-filter.readthedocs.org/en/latest/usage.html#the-filter)
+for details on the format for `filter_fields`.
+
 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.
@@ -225,12 +238,17 @@ query {
           edges {
             node {
               name
-            }
-          }
-        }
-
-      }
-    }
-  }
-}
+}}}}}}}
+```
+
+Or you can get only 'meat' ingredients containing the letter 'e':
+
+```graphql
+query {
+  # You can also use `category: "CATEGORY GLOBAL ID"`
+  allIngredients(nameIcontains: "e", categoryName: "Meat") {
+    edges {
+      node {
+        name
+}}}}
 ```