Build a Django elegant filter with dynamic number of fields

Viewed 145

I have a Django model which I'm doing a view for it, which filters results.

models.py:

Ingredient(models.Model):
    account = models.ForeignKey(Account, on_delete=models.CASCADE, null=False)
    brand = models.ForeignKey(Brand, on_delete=models.CASCADE, null=False)
    name = models.CharField(max_length=100, null=False, blank=False)
    cost = models.DecimalField(max_digits=14, decimal_places=2, null=False)

Now the point is that the search form (which has every field except account of course), none of its fields are required, you can filter either by just a single field or two or the three of them.

The problem is that I can't do:

Ingredient.objects.filter(account=account, brand=brand, name=name, cost=cost)

...because brand, name, and cost could be sent empty/null

How can I resolve this without making a filter line code for each filtering possibility?

1 Answers

We can make a utility function that filters out the Nones. For example:

from django.db.models import Q

def filter_without_none(**kwargs):
    return Q(**{k: v for k, v in kwargs.items() if v is not None and v != ''})

Now we can filter with:

Ingredient.objects.filter(
    filter_without_none(account=account, brand=brand, name=name, cost=cost)
)

If one (or multiple) of the values like account, brand and/or name are None, these will not be taken into account for filtering.

Related