Warm tip: This article is reproduced from stackoverflow.com, please click
django graphql graphene-python django-filters vue-apollo

How to stop Graphql + Django-Filters to return All Objects when Filter String is Empty?

发布于 2020-04-07 23:15:07

Using:

  • Django 3.x [ Django-Filters 2.2.0, graphene-django 2.8.0, graphql-relay 2.0.1 ]
  • Vue 2.x [ Vue-Apollo ]

I have a simple Birds Django-Model with Fields like name, habitat and applied different Filters on these Fields like icontains or iexact. My Goal was to apply a simple search field in my Frontend (Vue). So far it works, but whenever this Filter Value is empty or has blanks (see Example 3), Graphql returns all Objects.

My first approach was on the FrontEnd and to use some kind of logic on my input value, like when String is Empty/blank send isnull=true . But then i thought that Django should handle this in the first place. I guess this Issue relates to my filters (see Django < relay_schema.py) or in other words do i have to apply some kind of logic on these Filters?

In the moment i try to customize some filterset_class but it feels like this would maybe too much, maybe i missed something? So i ask here if maybe someone has some hints, therefore my question is:

How to stop Graphql + Django-Filters to return All Objects when Filter String is Empty?

GraphiQL IDE

Example 1


query {birdsNodeFilter (name_Iexact: "finch") {
  edges {
    node {
      id
      name
    }
     }
    }
}

Returns

{
  "data": {
    "birdsNodeFilter": {
      "edges": [
        {
          "node": {
            "id": "QmlyZHNOb2RlOjE=",
            "name": "Finch",
            "habitat": "Europe"
          }
        }
      ]
    }
  }
}

Fine for me!

Example 2


query {birdsNodeFilter (name_Iexact: "Unicorns") {
  edges {
    node {
      id
      name
      habitat
    }
     }
    }
}

Returns

{
  "data": {
    "birdsNodeFilter": {
      "edges": []
    }
  }
}

No Unicorns there - good

Example 3


query {birdsNodeFilter (name_Iexact: "") {
  edges {
    node {
      id
      name
    }
     }
    }
}

Return

{
  "data": {
    "birdsNodeFilter": {
      "edges": [
        {
          "node": {
            "id": "QmlyZHNOb2RlOjE=",
            "name": "Finch",
            "habitat": "Europe"
          }
        },
        {
          "node": {
            "id": "QmlyZHNOb2RlOjI=",
            "name": "Bald Eagle",
            "habitat": "USA"
          }
        },

<...And so on...>

Not fine for me!

Django

relay_schema.py


class BirdsNode(DjangoObjectType):
    class Meta:
        model = Birds
        filter_fields = {
            'id': ['iexact'],
            'name': ['iexact', 'icontains', 'istartswith', 'isnull'],
            'habitat': ['iexact', 'icontains', 'istartswith'],
        }
        interfaces = (relay.Node, )


class BirdQuery(graphene.ObjectType):
    birdConNode = relay.Node.Field(BirdsNode)
    birdsNodeFilter = DjangoFilterConnectionField(BirdsNode) 
Questioner
black_hole_sun
Viewed
55
black_hole_sun 2020-02-15 03:33

This is my Solution which worked in GraphiQL and in my Frontend VUE. I added a logic to the Birds2Query with def resolve_all_birds2 for each Filter (for testing purpose not on all Filter ) . Besides that i also added a ExtendedConnection for counting.

Note: i changed the class names from my former Question.

relay_schema.py

class ExtendedConnection(Connection):
    class Meta:
        abstract = True

    total_count = Int()
    edge_count = Int()
    name_check = ""

    def resolve_total_count(root, info, **kwargs):
        return root.length
    def resolve_edge_count(root, info, **kwargs):
        return len(root.edges)

class Birds2Node(DjangoObjectType):
    class Meta:

        model = Birds
        filter_fields =  {
            'id':  ['exact', 'icontains'],
            'name': ['exact', 'icontains', 'istartswith', 'iendswith'],
        }

        interfaces = (relay.Node, )
        connection_class = ExtendedConnection

class Birds2Query(ObjectType):
    birds2 = relay.Node.Field(Birds2Node)
    all_birds2 = DjangoFilterConnectionField(Birds2Node)


    def resolve_all_birds2(self, info, **kwargs):
        # Filtering for Empty/ Blank Values in Filter.Key.Value before returning queryset
         if 'name__icontains' in kwargs:
            nameIcon = kwargs['name__icontains']
            nameIconBool = bool(nameIcon.strip()) # if blanks turns False           
            if nameIconBool == False: # has blanks         
                return Birds.objects.filter(name=None)
            pass


        if 'name__istartswith' in kwargs:           
            nameIsta = kwargs['name__istartswith']
            nameIstaBool = bool(nameIsta.strip()) # if blanks turns False          
            if nameIstaBool == False: # has blanks         
                return Birds.objects.filter(name=None)
            pass
         return

GraphiQL Blockquote

Example 1

query {allBirds2 (name_Icontains:""){
  totalCount
    edgeCount
    edges {
      node {
        id
        name
        habitat
      }  
    }
  }
}

Stopped to return all Objects while Filter is blank.

{
  "data": {
    "allBirds2": {
      "totalCount": 0,
      "edgeCount": 0,
      "edges": []
    }
  }
}

Example 2 with blanks and one letter

query {allBirds2 (name_Icontains:" f  "){
  totalCount
    edgeCount
    edges {
      node {
        id
        name
        habitat
      }  
    }
  }
}

return - exactly what i wanted

{
  "data": {
    "allBirds2": {
      "totalCount": 1,
      "edgeCount": 1,
      "edges": [
        {
          "node": {
            "id": "QmlyZHMyTm9kZTox",
            "name": "Finch",
            "habitat": "Europe"
          }
        }
      ]
    }
  }
}