1

It seems that Django developers are aware about the fact that there's no way to filter on calculated fields like this in MySQL:

SomeModel.objects.extra(select={ 'total': 'SOME CALCULATION GOES HERE' }, where=['total < 10'])

OR

SomeModel.objects.extra(select={ 'total': 'SOME CALCULATION GOES HERE' }).filter(total__lt=10)

This was described in https://code.djangoproject.com/ticket/8784 but unfortunately it looks like it will take another year to implement with current speed. I understand the concern of development team that ideally they want to get rid of the .extra() because by writing pure SQL in that statement code becomes non DB-agnostic. I believe that dealing with calculated field is fairly common task in many projects and I currently faced the problem that I can't solve: I think the majority of community often uses this dirty workaround:

SomeModel.objects.extra(select={ 'total': 'SOME CALCULATION GOES HERE' }, where=['1 HAVING total < 10'])

and in fact it works, but only till the moment when you need to add filter on the result of the statement above:

SomeModel.objects.extra(select={ 'total': 'SOME CALCULATION GOES HERE' }, where=['1 HAVING total < 10']).filter(id__gt=300)

this doesn't work anymore, because it translates to the wrong SQL statement:

SELECT id, ..., (SOME CALCULATION) AS total
FROM some_table
WHERE (1 AND HAVING total < 10 AND id > 300)

What I want to achieve is to have some kind of restriction in custom model manager which would allow me to write code like this:

class SomeModelManager(models.Manager):
  def apply_total_restriction(self, restriction_num):
    return self.extra(select={ 'total': 'CALCULATION GOES HERE' }).filter(total__lt=restriction_num)

...

SomeModel.objects.apply_total_restriction(restriction_num).filter(created_by_id__exact=1)

Question is: does anybody know how to overcome this temporary (hopefully) restriction and to fulfill the task? Any possible alternatives?

4

1 に答える 1

0

I've found the solution: I thought it will be too slow to calculate that field again in WHERE clause, but when I've tried it it turned to be quite fast and no HAVING required. Below is the demonstration of what I meant:

SELECT (a + b / 2) as 'c', a, b FROM table WHERE (a + b / 2) >= 20 AND a != b

That's basically it - if you think you need HAVING, try to measure time with HAVING and compare it to time with calculating calculated field again and filter by it in WHERE and then make a conclusion.

于 2013-01-07T19:19:14.413 に答える