45

I've got a function built into my Django model class and I want to use that function to filter my query results.

  class service:
       ......
       def is_active(self):
            if datetime.now() > self.end_time:
                  return False
            return True

Now I want to use this function into my query filter, something like

nserv = service.objects.filter(is_active=True)

I know, for this simple 'is_active' case, I can directly make this comparision in filter query, but for more complex situations, that may not be possible. How should I make a query, based on custom functions?

3
  • 6
    By the way, you could do return datetime.now() <= self.end_time :-) Commented Oct 29, 2014 at 23:15
  • I had exactly the same problem! Even the function was called the same Commented Mar 4, 2018 at 19:24
  • 1
    The answers proposed here first do the query and then filter in it. For huge datasets and restrictive filtering, it would be more efficient to store the output of the function in a field of the model in order to do the filtering directly on the database query, and not afterwards. Commented Nov 1, 2018 at 14:14

4 Answers 4

32

I just had a similar issue. The problem was i had to return a QuerySet instance. A quick solution for me was to do something like this:

active_serv_ids = [service.id for service in Service.objects.all() if service.is_active()]
nserv = Service.objects.filter(id__in=active_serv_ids)

I'm pretty sure this is not the prettiest and performant way to do this, but it works for me.

A more verbose way of doing this would be:

active_serv_ids = []

for service in Service.objects.all():
    if service.is_active():
        active_serv_ids.append(service.id)

nserv = Service.objects.filter(id__in=active_serv_ids)
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you this is perfect. It is not yet a feature of django to "load the results" and do client side filtering, so this is the only way.
it isn't optimal since it does the query and only use part of it, but if you are sure that your filter will keep most of the results it's a good workaround
It works like a charm. Try to django filter as many as you can before the loop
21

I would suggest you to use a custom manager for your class, like this you could use :

nserv = service.objects.are_active()

This would be achieved with something like:

class ServiceManager(models.Manager):
    def are_active(self):
        # use your method to filter results
        return you_custom_queryset

See custom managers

3 Comments

# use your method to filter results is pretty much what the question is asking how to do.
@Ignacio - Actually this solution suits me as well. So I'll go with it just for sake of trying out something different.
Can you kindly provide a minimal reproducible example based on this question?
17

You may not be able to, instead you can post-process the queryset with a list comprehension or generator expression.

For example:

[x for x in Q if x.somecond()]

4 Comments

@A lee - :P I still haven't gotten up to speed at python lingo.
@Neo its ok, I've never hear people refer to them as LC or genex before either.
This does not return a Queryset, so it can not be used in some contexts (for example, to populate a select box in a form)
11

The answer by Ignacio is interesting, but it does not return a queryset. This one does:

def users_by_role(role):
    users = User.objects.all()
    ids = [user.id for user in users if user.role == role]
    return users.filter(id__in=ids)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.