Search code examples
djangodjango-rest-frameworkdjango-filter

How to exclude empty values when querying a DRF endpoint URL [Django]


Similar to an exclude filter that looks like this:

MyObject.objects.exclude(my_field="")

I know that I can modify the view to exclude the value in the queryset but that's not dynamic:

def get_queryset(self):
    return Client.objects.exclude(my_field="")

But I'm looking for a less static way of querying via URL. So something like this:

/api/object?my_field__isempty=False


Solution

  • @Hashem's answer is good, but you could also use Q statements

    from django.db.models import Q
    # Q allows for complex queries with:
    #   & (AND)
    #   | (OR)
    #   ^ (XOR)
    
    # Exclude method
    ModelName.objects.exclude(Q(field_name__isnull=True) | Q(field_name__exact=''))
    
    # Filter Method with ~ (NOT)
    ModelName.objects.filter(~Q(field_name__isnull=True) & ~Q(field_name__exact=''))
    

    Just throwing out another option, and knowing about Q statements is really beneficial
    Docs: https://docs.djangoproject.com/en/4.1/topics/db/queries/#complex-lookups-with-q


    Edit

    Here's an extra tidbit, you'll find this handy, you can use pointers and filter by a dictionary. The dictionary can be created dynamically

    filterDict = {'my_field':''}
    Client.objects.exclude(**filterDict)
    
    # Client.objects.exclude(**filterDict) === Client.objects.exclude(my_field=='')
    
    

    I'm not sure how you are doing Views, but if you have a "normal" one with the request object you you can fetch the GET parameters as a QueryDict:

    def myview_with_dynamic_pointer(request):
      print(request.GET)
    
      # Should work
      Client.objects.filter(**request.GET)
    
      # will work
      Client.objects.filter(**{k:v for k,v in request.GET.items()})
    
    def myview_with_dynamic_Q(request):
      print(request.GET)
    
      from django.db.models import Q
      dynamicquery = Q()
      
      for key, value in request.GET.items():
        dynamicquery = dynamicquery & Q({k:v})
    
        # Can also do OR:
        # dynamicquery = dynamicquery | Q({k:v})
    
      Client.objects.filter(dynamicquery)
    

    If you are using class based views, it'll be more like:

    class MyListView(ListView):
      model = MyModel
    
      def get_queryset(self):
        print(self.request.GET)
        return self.model.objects.filter(**self.request.GET)
    

    It might be a good idea to look over the GET before shoving it right into a filter.. Safety wise