Reputation: 5058
I am working on project where I need to pull the date from a table based on startdate and enddate. Here is the query
SELECT * FROM billing_lines WHERE (start_date, end_date) OVERLAPS ('2018-09-15 03:00:00', '2018-09-15 03:30:00');
I know i can use raw()
to execute the above query. But my question is , How to convert this into Django ORM query.
Upvotes: 2
Views: 982
Reputation: 477338
Two ranges [a1, b1) and [a2, b2) do not overlap if a1≥b2, or b1≤a2. We can negate this expression to know when two intervals overlap: a1<b2, and b1>a2.
Here start_date
and end_date
are a1 and b1 respectively, and the two values you wrote '2018-09-15 03:00:00'
and '2018-09-15 03:30:00'
are thus a2 and b2 respectively.
We can thus make a query like:
begin='2018-09-15 03:00:00'
end='2018-09-15 03:30:00'
Lines.objects.filter(
start_date__lt=end
end_date__gt=begin
)
This thus does not check if the (begin, end)
is "contained" in the (start_date, end_date)
, it checks if there is at least one element that is a member of both ranges.
Now the OVERLAP
function in PostgreSQL is a bit more complicated, since it automatically swaps the times in case the end_date
is greater than the start_date
, so we might need to use .annotate(..)
here:
from django.db.models import F
from django.db.models.functions import Greatest, Least
begin='2018-09-15 03:00:00'
end='2018-09-15 03:30:00'
Lines.objects.annotate(
d0=Least(F('start_date'), F('end_date')),
d1=Greatest(F('start_date'), F('end_date')),
).filter(
d0__lt=end
d1__gt=begin
)
This is still not exactly the same, since if d0
and d1
are the same, then the range is "inclusive", so we should consider this case as well:
Lines.objects.annotate(
d0=Least(F('start_date'), F('end_date')),
d1=Greatest(F('start_date'), F('end_date')),
).filter(
Q(d0__lt=end, d1__gt=begin) |
Q(d0=F('d1'), d0__gte=begin, d0__lt=end)
)
It also requires to "prepare" the begin
and end
(you should make sure that begin <= end
, and if not swap these, not because otherwise the method will fail, but because these are the "exact" specifications of the rather complicated OVERLAP
function).
Upvotes: 4
Reputation: 27523
YourModel.objects.filter(start_date__gte='2018-09-15 03:00:00',end_date__lte='2018-09-15 03:30:00')
this should do what you are looking for. Just replace your model name with the dummy provided.
Upvotes: 1