As a Django beginner I struggle with a very basic problem: Filter a table based on the date difference of two columns. I could solve this in raw SQL, but I would really want to use basic Django functions.
I have the following model:
from django.db import models
import datetime
class Race(models.Model):
__tablename__ = 'race'
name = models.CharField(max_length=200)
country = models.CharField(max_length=100, null=True)
start = models.DateField()
end = models.DateField()
and I want to extract races that last e.g. more than 5 days. I can somehow get a timediff column:
Race.objects.annotate(tdiff=F('end')-F('start')).first()
set1 = Race.objects.annotate(tdiff=F('end')-F('start')).all()
set1.first().tdiff
Now, how do I filter on this column, what I tried is:
min_diff = datetime.timedelta(5)
set1.filter(tdiff__gte=5).first()
set1.all().filter(tdiff__gte=min_diff)
set1.filter(tdiff__gte=min_diff).first()
but this all gives:
TypeError: expected string or bytes-like object
Then I considered using extra to get a where clause:
set2 = Race.objects.annotate(tdiff=F('end')-F('start'))
set2.first().tdiff
set2.all().extra(where=['tdiff>=5'])
resulting in:
ProgrammingError: column "tdiff" does not exist
Questions in the same direction include this one and this one but none really give a solution where you filter on a new column (here tdiff).
When finalizing this question I in the end did get the result that I wanted by:
Race.objects.filter(end__gte=F("start")+5)
print(Race.objects.filter(end__gte=F("start")+5).query)
but I still very much would like to know how to utilize this temporary column tdiff.
Thanks!
The accepted answer gives exactly what I wanted:
from django.db.models import DurationField, F, ExpressionWrapper
import datetime
set4 = Race.objects.annotate(
diff=ExpressionWrapper(F('end') - F('start'), output_field=DurationField())).filter(
diff__gte=datetime.timedelta(5))
len(set4)
# 364
len(Race.objects.filter(end__gte=F("start")+5))
# 364
This would do the magic:
from django.db.models import DurationField, F, ExpressionWrapper
import datetime
Race.objects.annotate(
diff=ExpressionWrapper(F('end') - F('start'), output_field=DurationField())
).filter(diff__gte=datetime.timedelta(5))
This will return all Race
instances whose duration is greater than or equal to 5
References:
ExpressionWrapper
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With