Filtering

Graphene integrates with django-filter (2.x for Python 3 or 1.x for Python 2) to provide filtering of results. See the usage documentation for details on the format for filter_fields.

This filtering is automatically available when implementing a relay.Node. Additionally django-filter is an optional dependency of Graphene.

You will need to install it manually, which can be done as follows:

# You'll need to django-filter
pip install django-filter>=2

Note: The techniques below are demoed in the cookbook example app.

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 for full details on the range of options available.

For example:

class AnimalNode(DjangoObjectType):
    class Meta:
        # Assume you have an Animal model defined with the following fields
        model = Animal
        filter_fields = ['name', 'genus', 'is_domesticated']
        interfaces = (relay.Node, )

class Query(ObjectType):
    animal = relay.Node.Field(AnimalNode)
    all_animals = DjangoFilterConnectionField(AnimalNode)

You could then perform a query such as:

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:

class AnimalNode(DjangoObjectType):
    class Meta:
        model = Animal
        # Provide more complex lookup types
        filter_fields = {
            'name': ['exact', 'icontains', 'istartswith'],
            'genus': ['exact'],
            'is_domesticated': ['exact'],
        }
        interfaces = (relay.Node, )

Which you could query as follows:

query {
  # Note that fields names become camelcased
  allAnimals(name_Icontains: "lion") {
    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.

However, you may find this to be insufficient. In these cases you can create your own FilterSet. You can pass it directly as follows:

class AnimalNode(DjangoObjectType):
    class Meta:
        # Assume you have an Animal model defined with the following fields
        model = Animal
        filter_fields = ['name', 'genus', 'is_domesticated']
        interfaces = (relay.Node, )


class AnimalFilter(django_filters.FilterSet):
    # Do case-insensitive lookups on 'name'
    name = django_filters.CharFilter(lookup_expr=['iexact'])

    class Meta:
        model = Animal
        fields = ['name', 'genus', 'is_domesticated']


class Query(ObjectType):
    animal = relay.Node.Field(AnimalNode)
    # We specify our custom AnimalFilter using the filterset_class param
    all_animals = DjangoFilterConnectionField(AnimalNode,
                                              filterset_class=AnimalFilter)

You can also specify the FilterSet class using the filterset_class parameter when defining your DjangoObjectType, however, this can’t be used in unison with the filter_fields parameter:

class AnimalFilter(django_filters.FilterSet):
    # Do case-insensitive lookups on 'name'
    name = django_filters.CharFilter(lookup_expr=['iexact'])

    class Meta:
        # Assume you have an Animal model defined with the following fields
        model = Animal
        fields = ['name', 'genus', 'is_domesticated']


class AnimalNode(DjangoObjectType):
    class Meta:
        model = Animal
        filterset_class = AnimalFilter
        interfaces = (relay.Node, )


class Query(ObjectType):
    animal = relay.Node.Field(AnimalNode)
    all_animals = DjangoFilterConnectionField(AnimalNode)

The context argument is passed on as the request argument in a django_filters.FilterSet instance. You can use this to customize your filters to be context-dependent. We could modify the AnimalFilter above to 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'])

    class Meta:
        model = Animal
        fields = ['name', 'genus', 'is_domesticated']

    @property
    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.

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:

query {
  group(id: "xxx") {
    users(orderBy: "-created_at") {
      xxx
    }
  }
}