Skip to main content

Advanced filters for Graphene

Project description

Graphene-Django-Filter

CI Coverage Status PyPI version License: MIT

This package contains advanced filters for graphene-django. The standard filtering feature in graphene-django relies on the django-filter library and therefore provides the flat API without the ability to use logical operators such as and, or and not. This library makes the API nested and adds logical expressions by extension of the DjangoFilterConnectionField field and the FilterSet class. Also, the library provides some other convenient filtering features.

Installation

# pip
pip install graphene-django-filter
# poetry
poetry add graphene-django-filter

Requirements

  • Python (3.7, 3.8, 3.9, 3.10)
  • Graphene-Django (2.15)

Features

Nested API with the ability to use logical operators

To use, simply replace all DjangoFilterConnectionField fields with AdvancedDjangoFilterConnectionField fields in your queries. Also,if you create custom FilterSets, replace the inheritance from the FilterSet class with the inheritance from the AdvancedFilterSet class. For example, the following task query exposes the old flat API.

import graphene
from django_filters import FilterSet
from graphene_django import DjangoObjectType
from graphene_django.filter import DjangoFilterConnectionField

class TaskFilter(FilterSet):
    class Meta:
        model = Task
        fields = {
            'name': ('exact', 'contains'),
            'user__email': ('exact', 'contains'),
            'user__first_name': ('exact', 'contains'),
            'user__last_name': ('exact', 'contains'),
        }

class UserType(DjangoObjectType):
    class Meta:
        model = User
        interfaces = (graphene.relay.Node,)
        fields = '__all__'

class TaskType(DjangoObjectType):
    user = graphene.Field(UserType)

    class Meta:
        model = Task
        interfaces = (graphene.relay.Node,)
        fields = '__all__'
        filterset_class = TaskFilter

class Query(graphene.ObjectType):
    tasks = DjangoFilterConnectionField(TaskType)

The flat API in which all filters are applied using the and operator looks like this.

{
  tasks(
    name_Contains: "important"
    user_Email_Contains: "john"
    user_FirstName: "John"
    user_LastName: "Dou"
  ){
    edges {
      node {
        id
        name
      }
    }
  }
}

After replacing the field class with the AdvancedDjangoFilterConnectionField and the FilterSet class with the AdvancedFilterSet the API becomes nested with support for logical expressions.

import graphene
from graphene_django_filter import AdvancedDjangoFilterConnectionField, AdvancedFilterSet

class TaskFilter(AdvancedFilterSet):
    class Meta:
        model = Task
        fields = {
            'name': ('exact', 'contains'),
            'user__email': ('exact', 'contains'),
            'user__first_name': ('exact', 'contains'),
            'user__last_name': ('exact', 'contains'),
        }

class Query(graphene.ObjectType):
    tasks = AdvancedDjangoFilterConnectionField(TaskType)

For example, the following query returns tasks which names contain the word "important" or the user's email address contains the word "john" and the user's last name is "Dou" and the first name is not "John". Note that the operators are applied to lookups such as contains, exact, etc. at the last level of nesting.

{
  tasks(
    filter: {
      or: [
        {name: {contains: "important"}}
        {
            and: [
              {user: {email: {contains: "john"}}}
              {user: {lastName: {exact: "Dou"}}}
            ]
        }
      ]
      not: {
        user: {firstName: {exact: "John"}}
      }
    }
  ) {
    edges {
      node {
        id
        name
      }
    }
  }
}

The same result can be achieved with an alternative query structure because within the same object the and operator is always used.

{
  tasks(
    filter: {
      or: [
        {name: {contains: "important"}}
        {
          user: {
            email: {contains: "john"}
            lastName: {exact: "Dou"}
          }
        }
      ]
      not: {
        user: {firstName: {exact: "John"}}
      }
    }
  ){
    edges {
      node {
        id
        name
      }
    }
  }
}

The filter input type has the following structure.

input FilterInputType {
  and: [FilterInputType]
  or: [FilterInputType]
  not: FilterInputType
  ...FieldLookups
}

For more examples, see tests.

Full text search

Django provides the API for PostgreSQL full text search. Graphene-Django-Filter inject this API into the GraphQL filter API. To use, add full_text_search lookup to fields for which you want to enable full text search. For example, the following type has full text search for first_name and last_name fields.

import graphene
from graphene_django import DjangoObjectType
from graphene_django_filter import AdvancedDjangoFilterConnectionField

class UserType(DjangoObjectType):
    class Meta:
        model = User
        interfaces = (graphene.relay.Node,)
        fields = '__all__'
        filter_fields = {
            'email': ('exact', 'startswith', 'contains'),
            'first_name': ('exact', 'contains', 'full_text_search'),
            'last_name': ('exact', 'contains', 'full_text_search'),
        }

class Query(graphene.ObjectType):
    users = AdvancedDjangoFilterConnectionField(UserType)

Since this feature belongs to the AdvancedFilterSet, it can be used in a custom FilterSet. The following example will work exactly like the previous one.

import graphene
from graphene_django import DjangoObjectType
from graphene_django_filter import AdvancedDjangoFilterConnectionField, AdvancedFilterSet

class UserFilter(AdvancedFilterSet):
    class Meta:
        model = User
        fields = {
            'email': ('exact', 'startswith', 'contains'),
            'first_name': ('exact', 'contains', 'full_text_search'),
            'last_name': ('exact', 'contains', 'full_text_search'),
        }

class UserType(DjangoObjectType):
    class Meta:
        model = User
        interfaces = (graphene.relay.Node,)
        fields = '__all__'
        filterset_class = UserFilter

class Query(graphene.ObjectType):
    users = AdvancedDjangoFilterConnectionField(UserType)

Full text search API includes SearchQuery, SearchRank, and Trigram filters. SearchQuery and SearchRank filters are at the top level. If some field has been enabled for full text search then it can be included in the field array. The following queries show an example of using the SearchQuery and SearchRank filters.

{
  users(
    filter: {
      searchQuery: {
        vector: {
          fields: ["first_name"]
        }
        query: {
          or: [
            {value: "Bob"}
            {value: "Alice"}
          ]
        }
      }
    }
  ){
    edges {
      node {
        id
        firstName
        lastName  
      }
    }
  }
}
{
  users(
    filter: {
      searchRank: {
        vector: {fields: ["first_name", "last_name"]}
        query: {value: "John Dou"}
        lookups: {gte: 0.5}
      }
    }
  ){
    edges {
      node {
        id
        firstName
        lastName  
      }
    }
  }
}

Trigram filter belongs to the corresponding field. The following query shows an example of using the Trigram filter.

{
  users(
    filter: {
      firstName: {
        trigram: {
          value: "john"
          lookups: {gte: 0.85}
        }
      }
    }
  ){
    edges {
      node {
        id
        firstName
        lastName  
      }
    }
  }
}

Input types have the following structure.

input SearchConfigInputType {
  value: String!
  isField: Boolean
}
enum SearchVectorWeight {
  A
  B
  C
  D
}
input SearchVectorInputType {
  fields: [String!]!
  config: SearchConfigInputType
  weight: SearchVectorWeight
}
enum SearchQueryType {
  PLAIN
  PHRASE
  RAW
  WEBSEARCH
}
input SearchQueryInputType {
  value: String
  config: SearchConfigInputType
  and: [SearchQueryInputType]
  or: [SearchQueryInputType]
  not: SearchQueryInputType
}
input SearchQueryFilterInputType {
  vector: SearchVectorInputType!
  query: SearchQueryInputType!
}
input FloatLookupsInputType {
  exact: Float
  gt: Float
  gte: Float
  lt: Float
  lte: Float
}
input SearchRankWeightsInputType {
  D: Float
  C: Float
  B: Float
  A: Float
}
input SearchRankFilterInputType {
  vector: SearchVectorInputType!
  query: SearchQueryInputType!
  lookups: FloatLookupsInputType!
  weights: SearchRankWeightsInputType
  coverDensity: Boolean
  normalization: Int
}
enum TrigramSearchKind {
  SIMILARITY
  DISTANCE
}
input TrigramFilterInputType {
  kind: TrigramSearchKind
  lookups: FloatLookupsInputType!
  value: String!
}

For more examples, see tests.

Settings

The library can be customised using settings. To add settings, create a dictionary with name GRAPHENE_DJANGO_FILTER in the project’s settings.py. The default settings are as follows.

GRAPHENE_DJANGO_FILTER = {
    'FILTER_KEY': 'filter',
    'AND_KEY': 'and',
    'OR_KEY': 'or',
    'NOT_KEY': 'not',
}

To read the settings, import them from the conf module.

from graphene_django_filter.conf import settings

print(settings.FILTER_KEY)

The settings object also includes fixed settings, which depend on the user's environment. IS_POSTGRESQL determinate that current database is PostgreSQL and HAS_TRIGRAM_EXTENSION that pg_trgm extension is installed.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

graphene-django-filter-0.6.3.tar.gz (18.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

graphene_django_filter-0.6.3-py3-none-any.whl (18.4 kB view details)

Uploaded Python 3

File details

Details for the file graphene-django-filter-0.6.3.tar.gz.

File metadata

  • Download URL: graphene-django-filter-0.6.3.tar.gz
  • Upload date:
  • Size: 18.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/34.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.9 tqdm/4.63.1 importlib-metadata/4.11.3 keyring/23.5.0 rfc3986/2.0.0 colorama/0.4.4 CPython/3.9.12

File hashes

Hashes for graphene-django-filter-0.6.3.tar.gz
Algorithm Hash digest
SHA256 828f29074c15e17a5632be02a773b6022ee53b99b8a6d1d5683ed0a5802a6443
MD5 c22cdcc3ad1d0b821c44062c29e04940
BLAKE2b-256 c1072145fa00c53a712b3d8240f7bc5f6c359dd1fbcd4c6337385f3558564027

See more details on using hashes here.

File details

Details for the file graphene_django_filter-0.6.3-py3-none-any.whl.

File metadata

  • Download URL: graphene_django_filter-0.6.3-py3-none-any.whl
  • Upload date:
  • Size: 18.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/34.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.9 tqdm/4.63.1 importlib-metadata/4.11.3 keyring/23.5.0 rfc3986/2.0.0 colorama/0.4.4 CPython/3.9.12

File hashes

Hashes for graphene_django_filter-0.6.3-py3-none-any.whl
Algorithm Hash digest
SHA256 879ce2756ce1622ead5f4b6742668ff712763462b1adc62664907b0e1df100ed
MD5 eef6a624a9d43f9e0d24a71074e7e62a
BLAKE2b-256 a40326abf010a556732b8ec3c74307bc9e0fc0318bb0064d772a3b4c94d39631

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page