How do i disregard a django filter field if the field is empty?

234 Views Asked by At

So, i started to code a filtering system to my django application. It goes like this

class Person(models.Model):
     first_name = models.CharField(max_length = 50)
     middle_name = models.CharField(max_length = 50)
     last_name = models.CharField(max_length = 50)

i wanted to filter which Person has a field containing x value if other fields are empty, like this algorithm

results = Person.objects.filter(first_name__contains = "a")

but if other fields are not empty, i want to filter it like this

results = Person.objects.filter(first_name__contains = "a", middle_name__contains = "a", last_name__contains = "a")

depending on which fields have none empty value.

my first attempt was this:

if first_name != "":
     results = Person.objects.filter(first_name__contains = first_name)
if middle_name != "":
     results = results.filter(middle_name__contains = middle_name)
if last_name != "":
     results = results.filter(last_name__contains = last_name)

My problem here is that it filters it sequentially, so if i filter the middle_name only or last_name only, it would return me an error since results is not yet defined, could anyone help me with this?

is there an algorithm that goes like this? results = Person.objects.filter(first_name__contains = first_name if first_name != "" else JUST REMOVE THIS FILTER)? because if it filters a "" it would return all your data. Thank you in advance

2

There are 2 best solutions below

4
On BEST ANSWER

You can build up a queryset in stages

qs = Person.objects.all()
if first_name != "":
   qs = qs.filter( first_name__contains=first_name )
if middle_name != "":
   qs = qs.filter( middle_name__contains=middle_name )
if last_name != "":
   qs = qs.filter( last_name__contains=last_name )

You can do much the same with Q objects for OR-type matching (it's a bit fiddlier)

qs = Person.objects.all()
q = None
for op, name in (( "first_name__contains", first_name),
                 ( "middle_name__contains", middle_name),
                 ( "last_name__contains",  last_name)) :

    if name != "":
        if q:
            q = q | Q( **{ op: name} )
        else
            q =     Q( **{ op: name} )

if q:
    qs = qs.filter(q)
2
On

In simple words, you want to do an ORing between first_name, last_name, and middle_name, when querying for a specific provided string.

I highly recommend you using Q Object Lookups in Django

your code will be simpler like the following:

text = 'a'
results = Person.objects.filter(Q(first_name__contains=text) | Q(middle_name__contains=text) | Q(last_name__contains=text))