Home > Enterprise >  Is there a Djangonic way to get Django objects from a list defaulting to all() if the list is empty?
Is there a Djangonic way to get Django objects from a list defaulting to all() if the list is empty?

Time:05-04

For example something like MyModel.objects.filter(some_value___in=[1,2], ignore_null_some_value=True) would return MyModels with some_values 1 or 2, but MyModel.objects.filter(some_value__in=[], ignore_null_some_value=True) would return all instances of MyModel?

My best effort so far is to do a second db query earlier to make sure the list is populated, e.g.:

values = <list> or MyModel.objects.values_list('some_value', flat=True))
MyModel.objects.filter(some_value__in=values)

But this feels inefficient.

CodePudding user response:

You could do

q_all = MyModel.objects.all()
filtered = q_all.filter(...)
if filtered.count() == 0:
    return q_all
else:
    return filtered

The point is that the first query (q_all) is not executed before you ask for count(). Then you know how many there are and you can return q_all in case. From the docs (https://docs.djangoproject.com/en/4.0/ref/models/querysets/):

Internally, a QuerySet can be constructed, filtered, sliced, and generally passed around without actually hitting the database. No database activity actually occurs until you do something to evaluate the queryset.

Therefore, only one query is executed effectively.

CodePudding user response:

Your way will be very inefficient, because this part has to do set comparison, which will be expensive in the DB:

MyModel.objects.filter(some_value__in=values)

You can simply do:

result = <list> or MyModel.objects.all()

You might however wanna do some more complicated things in between. Maybe you wanna return objects with some value__in=[1,2], but still return everything in case those are not found in the database either:

qs = MyModel.objects.filter(some_value__in=[1, 2])
result = qs if qs.exists() else MyModel.objects.all()

The qs.exists() is much more efficient than bool(qs), because that pulls all the data from the DB. qs.exists() only sends a single boolean and is much lighter for the DB.

CodePudding user response:

You will evaluate query twice anyway so I suggest to do just:

filtered_qs = MyModel.objects.filter(some_value___in=[1,2])

return filtered_qs or MyModel.objects.all()
  • Related