user11928022
user11928022

Reputation:

How to create custom filter using graphene-django and Relay that use multiple fields of different types?

Problem

Hello,

I'm using Graphene Django and Graphene Relay with Python 3.7. I'm trying to make a custom filter for the requests in GraphQL using Django filters.

My table looks like this:

| id(type: int) | flow(type: varchar) | datetime(type: datetime) | duration(type: int) |
|---------------|---------------------|--------------------------|---------------------|
| 1             | aaa                 | 2019-07-06 08:59:00      | 113095465           |
| 2             | xxx                 | 2019-07-06 08:59:00      | 113095465           |
| 3             | bbb                 | 2019-07-06 08:59:00      | 113095465           |

I want to be able to execute this kind of SQL request using GraphQL:

SELECT * FROM tablename WHERE datetime <= "2019-07-06 09:00:00" AND DATE_ADD(datetime, INTERVAL duration / 1000 SECOND) >= "2019-07-06 08:59:00";

Using a graphql query like : (of course the dates would be in Python DateTime Format)

{
  allTable(startDate: "2019-07-06 08:59:00", endDate: "2019-07-06 09:00:00") {
    edges {
      node {
        id
        name
      }
    }
  }
}

Like this is also fine as long as it works :

{
  allTable(timeRange: "{'start':'2019-07-06 08:59:00', 'end': '2019-07-06 09:00:00'}") {
    edges {
      node {
        id
        name
      }
    }
  }
}

Code

models.py

from django.db import models

class TableName(models.Model):
    name = models.CharField()
    datetime = models.DateTimeField()
    duration = models.BigIntegerField()
    class Meta:
        managed = False
        db_table = 'tablename'

schema.py

from graphene import relay, ObjectType
from graphene_django import DjangoObjectType
from graphene_django.filter import DjangoFilterConnectionField
import django_filters

from .models import TableName
from .fieldList import fields_table

class TableFilter(django_filters.FilterSet):
    class Meta:
        model = TableName
        fields = fields_table

class TableType(DjangoObjectType):
    class Meta:
        model = TableName
        filterset_class = TableFilter
        interfaces = (relay.Node,)

class Query(ObjectType):
    table = relay.Node.Field(TableType)
    all_Table = DjangoFilterConnectionField(TableType)

I already managed to add custom filter on field and get this filter in GraphQL :

class TableFilter(django_filters.FilterSet):
    yolo = django_filters.NumberFilter(field_name="duration", lookup_expr='gt')
    ...

But I don't know how to add this type of custom filter.

So far I have seen those documentations: django_filters documentation, Graphene documentation and Django documentation about querysets but couldn't understand everything and could not find an example that match what I need :/

Thanks for taking the time to help :)

Solution(not optimized)

I ended up using this to get what I want but it's not optimized since I can't get all the filters provided by the DjangoFilterConnectionField.

If you have a better solution I will gladly take it since pagination and filters are nice !

schema.py

from graphene import relay, ObjectType, List
from graphene_django import DjangoObjectType
from graphene_django.filter import DjangoFilterConnectionField
import django_filters
import graphene

from .models import TableName
from .fieldList import fields_table

class TableFilter(django_filters.FilterSet):
    class Meta:
        model = TableName
        fields = fields_table

class TableType(DjangoObjectType):
    class Meta:
        model = TableName
        filterset_class = TableFilter
        interfaces = (relay.Node,)

class Query(ObjectType):
    table = relay.Node.Field(TableType)
    all_Table = DjangoFilterConnectionField(TableType)
    table_range = List(TableType, startDate=graphene.Argument(
        graphene.DateTime, required=True), endDate=graphene.Argument(graphene.DateTime, required=True))

    def resolve_trace_range(root, info, startDate, endDate):
        return TableType.objects.raw("""
        SELECT * FROM tablename WHERE
        datetime <= '{}'
        AND DATE_ADD(datetime, INTERVAL duration / 1000 SECOND) >= '{}';
        """.format(str(startDate), str(endDate)))

This allows me to do requests like :

{
  tableRange(startDate: "2019-08-19T08:31:24.315Z", endDate: "2019-08-19T09:31:24.315Z") {
      id
      flow
      datetime
      duration
  }
}

Note that the DateTime is formatted using ISO-8601. In javascript you can get it using :

now = new Date().toISOString();
console.log(now); 
// Output: 2019-08-19T09:34:19.075Z

Upvotes: 15

Views: 3273

Answers (1)

Jackson Bakari
Jackson Bakari

Reputation: 63

You can just create a custom graphene Object like this:

class TableFilterInputObjects(graphene.InputObjectType):
    start_date = graphene.Date()
    end_date = graphene.Date()

then you can use it simply like this:

    class Query(ObjectType):
       table_range = List(TableType, filter = TableFilterInputObjects(required = True))    

        def resolve_trace_range(root, info,filter):
            
            filters = Q()
            if filter is not None:
                if filter.start_date is not None:
                    filters &=Q(datetime__gte = start_date)

                if filter.end_date is not None:
                    filters &=Q(datetime__lte = end_date)

            return TableType.objects.filter(filters).all()

and don't forget to import Q:

from django.db.models import Q

Upvotes: 1

Related Questions