Hellnar
Hellnar

Reputation: 64783

Django, query filtering from model method

I have these models:

def Foo(Models.model):
    size = models.IntegerField()
    # other fields

    def is_active(self):
         if check_condition:
              return True
         else:
              return False

def Bar(Models.model):
     foo = models.ForeignKey("Foo")
     # other fields

Now I want to query Bars that are having active Foo's as such:

Bar.objects.filter(foo.is_active())

I am getting error such as

SyntaxError at /
('non-keyword arg after keyword arg'

How can I achieve this?

Upvotes: 79

Views: 58861

Answers (6)

oldtechaa
oldtechaa

Reputation: 1524

One option is to use django-denorm to essentially dynamically create a model field:

@denorm.denormalized(models.BooleanField, null=True, default=False)
@denorm.depend_on_related("Bar")
def is_active(self):
    # return something

Then you can filter by foo__is_active=True.

After adding this, use manage.py makemigrations and manage.py migrate and manage.py denorm-init to prepare the database. This may not be the best solution because models should be primarily representing the database structure, not doing much work themselves, but it's what my predecessor did.

Upvotes: 0

Hans
Hans

Reputation: 36

Apparently, and as Ignacio Vazquez-Abrams pointed out, you cannot query against model methods in a view. Instead, I would apply the required filter logic in the view.

For instance, when check_condition is a date range:

class BarView(ListView):
    model = Bar
    template_name = "app/bar.html"

    def get_queryset(self):
        # Filter for a condition (here the condition is a date range)
        now = timezone.now().date()
        return Bar.objects.filter(foo__start_date__lte=now, foo__end_date__gte=now)

Upvotes: 0

Milo Wielondek
Milo Wielondek

Reputation: 4352

You could also use a custom manager. Then you could run something like this:

Bar.objects.foo_active()

And all you have to do is:

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

Check out the docs.

Upvotes: 34

Tomas Tomecek
Tomas Tomecek

Reputation: 6456

I had similar problem: I am using class-based view object_list and I had to filter by model's method. (storing the information in database wasn't an option because the property was based on time and I would have to create a cronjob and/or... no way)

My answer is ineffective and I don't know how it's gonna scale on larger data; but, it works:

q = Model.objects.filter(...)...
# here is the trick
q_ids = [o.id for o in q if o.method()]
q = q.filter(id__in=q_ids)

Upvotes: 26

Gabriel Hurley
Gabriel Hurley

Reputation: 40042

You can't filter on methods, however if the is_active method on Foo checks an attribute on Foo, you can use the double-underscore syntax like Bar.objects.filter(foo__is_active_attribute=True)

Upvotes: 10

Ignacio Vazquez-Abrams
Ignacio Vazquez-Abrams

Reputation: 798456

You cannot query against model methods or properties. Either use the criteria within it in the query, or filter in Python using a list comprehension or genex.

Upvotes: 45

Related Questions