Reputation: 64783
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
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
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
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
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
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
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