장고 쿼리셋 도큐먼트에 날짜와 관련해서 여러가지 편리한 필드룩업(fieid lookup)을 가지고 있다. 무심코 사용하다가 실수 할수 있는 것들을 아느선에서 정리하고자 한다.
https://docs.djangoproject.com/en/3.2/ref/models/querysets/
import datetime
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2005, 3, 31)
Entry.objects.filter(pub_date__range=(start_date, end_date))
pub_date가 Date 컬럼이면 sql query는 아래와 상응된다.
SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';
문제는 pub_date가 DateTime 타입이면 마지막 날짜에서 문제가 된다.
SELECT ... WHERE pub_date BETWEEN '2005-01-01 00:00:00' and '2005-03-31 00:00:00';
마지막 날짜가 '2005-03-31 00:00:00' 되기에 3월 31일 포함 되지 않는다. 개발자의 의도는 31일까지 포함을 예상했지만 DateTime 타입에서는 포함되지 않을 수 있다.
qs = TeacherAttendance.objects.filter(
center_id=16,
date_attended__year=2020,
date_attended__month=6,
date_attended__day=20
)
SELECT .... FROM `erp_teacherattendance`
WHERE (`erp_teacherattendance`.`center_id` = 16
AND `erp_teacherattendance`.`date_attended` BETWEEN 2020-01-01 AND 2020-12-31
AND EXTRACT(MONTH FROM `erp_teacherattendance`.`date_attended`) = 6
AND EXTRACT(DAY FROM `erp_teacherattendance`.`date_attended`) = 20)
필드 룩업 __year만 사용하면
BETWEEN 2020-01-01 AND 2020-12-31
필드 룩업 __month or __day 사용하면
AND EXTRACT(MONTH FROM `erp_teacherattendance`.`date_attended`) = 6
AND EXTRACT(DAY FROM `erp_teacherattendance`.`date_attended`) = 20)
EXTRACT로 치환되기에 인덱싱을 타지 않는다..
이거 이외에 EXTRACT로 치환되는 필드 룩업들이 있으니 잘 살펴 봐야할거 같다.
>>> date_start
'2021-01-01 00:00'
>>> date_start_utc = datetime.strptime(date_start, '%Y-%m-%d %H:%M').astimezone(pytz.utc)
>>> album_qs = Album.objects.filter(center_id=16)
>>> a = album_qs.filter(created__date=date_start_utc)
>>> str(a.query)
created는 DateTime 타입이고, 타임존을 적용한 날짜객체(date_start_utc)로
__date 필터 사용 시, sql 쿼리문은 CONVERT_TZ() 사용하게 되어 인덱스를 타지 않는다.
타임존을 적용한 날짜 객체로 필터를 사용하게 되면 일반적으로 CONVERT_TZ() 하지 않는데 __date 필터를 사용하면 무조건 CONVERT_TZ() 치환 한다.
"SELECT ... WHERE (`albums_album`.`center_id` =
16 AND DATE(CONVERT_TZ(`albums_album`.`created`, 'UTC', Asia/Seoul)) = 2021-01-01)"
장고 queryset에서 제공하는 편리한 날짜 비교 룩업을 사용은 지양하고 수고로움이 있더라도 비교연산자를 사용하는게 더 좋아 보인다. (gt, gte, lt, lte...)