[Django]-Possible to filter the queryset after querying? django

42👍

Yes, you can reuse existing querysets.

everyone = User.objects.filter(is_active=True)
active_not_deleted = everyone.filter(is_deleted=False)
active_is_deleted = everyone.filter(is_deleted=True)

This is not really making anything faster though, in fact, this code block won’t even execute a query against the database because Django QuerySets are lazily evaluated. What I means is that it won’t send the query to the database until you actually need the values. Here’s an example that will talk to the database.

everyone = User.objects.filter(is_active=True)  # Building SQL...
active_not_deleted = everyone.filter(is_deleted=False)  # Building SQL...
active_is_deleted = everyone.filter(is_deleted=True)  # Building SQL...

# Example of the whole queryset being evaluated
for user in everyone:
    # This will execute the query against the database to return the list of users
    # i.e. "select * from user where is_active is True;"
    print(user)

# Example of using iterator to evaluate one object at a time from the queryset.
for user in active_not_deleted.iterator():
    # This will execute the query for each result, so it doesn't
    # load everything at once and it doesn't cache the results.
    # "select * from user where is_active is True and is_deleted is False limit 1 offset 0;"
    # The offset is incremented on each loop and another query is sent to retrieve the next user in the list.
    print(user)

Recommend reading:

As an addition to this answer, you could make a single query and then filter in Python if you really wanted. Mind you, you could not do subsequent filtering on the lists because they are not QuerySets.

everyone = User.objects.filter(is_active=True)
active_not_deleted = list(filter(lambda user: user.is_deleted is False), list(everyone))
active_is_deleted = list(filter(lambda user: user.is_deleted is True), list(everyone))

In this last example, everyone is a queryset, and active_not_deleted and active_is_deleted are Python lists of User objects. The everyone queryset will only be evaluated once in the first list(everyone) call, and then the results are cached.

7👍

1. chain filter method

not_deleted = User.objects.filter(active=True).filter(is_deleted=False)

@Cory Madden already answered. User.objects.filter(active=True) returns Queryset. So you can add filter method. active_users.filter(is_deleted=False)

2. using Q method

from django.db.models import Q

not_deleted = User.objects.filter(Q(active=True) & Q(is_deleted=False)

It is easier to manage your complicated queryset. What if you want to filter userID is not 3? you can use Q simplye like User.objects.filter(Q(active=True) & ~Q(id = 3))


Answer for your comment,

Using Q or not, it has same raw query.

SELECT ... FROM ... 
WHERE ("auth_user"."active" = True AND "auth_user"."is_deleted" = False)

Database performance is relating to how often you hit database to extract data or if you use a heavy method like ‘Join’ when you extract something by FK relationship. So Using Q or not doesn’t give you performance difference, because it has same query sentence.

Additionally,

user = User.objects.filter(active=True)
not_deleted = User.objects.filter(active=True).filter(is_deleted=False)

user = User.objects.filter(active=True)
not_deleted = user.filter(is_deleted=False)

would not give you performance difference.

Queryset is lazy. user and not_deleted variables have just queryset string. It doesn’t hit the database right away when you define variable like above. Anyway, you will hit three times for each variable.

4👍

The best you can do is:

active_users = User.objects.filter(active=True)
not_deleted = active_users.filter(is_deleted=False)
deleted = active_users.filter(is_deleted=True)

So the answer to your question may be yes, if I understand it correctly.

1👍

You can filter Queryset so much time that you want, because filter() returns a new Queryset so after filtering you get filtered Queryset and you can do filter or orderby and another methods that return new QuerySets

So you can do this:

active = User.objects.filter(active=True)
deleted = active.filter(is_deleted=True)
not_deleted = active.filter(is_deleted=False)

All it is because User.objects – is Queryset and User.objects.filter also return Queryset.

Leave a comment