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 +}}}} ```