Django查询年度数据时DateTimeField naive datetime警告解决方法
问题背景
我需要过滤查询集(queryset)获取当年的全部帖子,最初编写的代码如下:
def thisYearQuerySet(objects): start_day = datetime.date(datetime.date.today().year, 1, 1) end_day = datetime.date(datetime.date.today().year, 12, 31) return objects.filter(date__range=[start_day, end_day])
运行时Django抛出提示,称start_day和end_day的声明可能与django.utils.timezone冲突,该问题不影响核心功能,但抛出的警告非常影响使用体验。
我不想通过关闭Django全局警告的方式消除提示,希望了解如何通过django.utils相关工具生成带时区信息的年度首日与末日来解决该问题。
完整警告内容
RuntimeWarning: DateTimeField Model.date received a naive datetime (2021-01-01 00:00:00) while time zone support is active. RuntimeWarning: DateTimeField Model.date received a naive datetime (2021-12-31 00:00:00) while time zone support is active.
解决方案
需要设置对应时区才能消除该警告,以下是可直接复用的项目时间处理模块,包含年/月/周/日等多维度的查询集过滤、时间转换工具:
import pytz import datetime from django.utils import timezone from django.conf import settings from django.db.models import Q from django.utils.dateparse import parse_datetime # MM/DD/YY 格式时间转换 def when(create): return '%s/%s/%s' % (create.month, create.day, create.year) # 获取当前格式化时间 def current(): return when(timezone.now()) # 生成相对时间描述 # create: 对象创建日期 # now: 当前时间 # li: 时间单位字符串列表(可自定义语言) # lst: 时间后缀(可自定义语言) # long: 展示的时间单位长度 def howLongAgo( create, now, li=[ '秒', '分', '时', '天', '周', '月', '年', ], lst='之前', long=2 ): dif = create - now sec = dif.days * 24 * 60 * 60 + dif.seconds minute = sec // 60 sec %= 60 hour = minute // 60 minute %= 60 day = hour // 24 hour %= 24 week = day // 7 day %= 7 month = (week * 7) // 30 week %= 30 year = month // 12 month %= 12 s = [] for ii, tt in enumerate([sec, minute, hour, day, week, month, year]): ss = li[ii] if tt != 0: s.append(str(tt) + ss) return ' '.join(list(reversed(s))[:long]) + lst # 日期转日期时间类型 def dateToDatetime(li): res = [] for ar in li: res.append(datetime.datetime.combine(ar, datetime.datetime.min.time())) return res # 生成连续日期范围迭代器 def datespan(startDate, endDate, delta=datetime.timedelta(days=1)): currentDate = startDate while currentDate < endDate: yield currentDate currentDate += delta # ========== 查询集过滤工具 ========== # 当月数据查询 def thisMonthQuerySet(objects): today = timezone.now() year = today.year start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(year, today.month, 1))) end_day = start_day + datetime.timedelta(30) return objects.filter(date__range=[start_day, end_day]) # 上月数据查询 def lastMonthQuerySet(objects): today = timezone.now() year = today.year month = today.month - 1 if month == 0: year -= 1 month = 12 start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(year, month, 1))) end_day = start_day + datetime.timedelta(30) return objects.filter(date__range=[start_day, end_day]) # 当年数据查询(修复时区警告版本) def thisYearQuerySet(objects): today = timezone.now() start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year, 1, 1))) end_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year + 1, 1, 1))) return objects.filter(date__range=[start_day, end_day]) # 去年数据查询 def lastYearQuerySet(objects): today = timezone.now() start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year - 1, 1, 1))) end_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year, 1, 1))) return objects.filter(date__range=[start_day, end_day]) # 近四个季度数据查询 def lastQuaterQuerySet(objects): today = timezone.now() four_month_before = today - timezone.timedelta(30 * 4) start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(four_month_before.year, four_month_before.month, 1))) return objects.filter(date__gte=start_day) # ========== 时间范围获取工具 ========== # 获取上周日期范围 def getLastWeek(): date = datetime.date.today() end_day = date - datetime.timedelta(date.weekday()) start_day = end_day - datetime.timedelta(7) return dateToDatetime([day for day in datespan(start_day, end_day)]) # 获取本周日期范围 def getThisWeek(): date = datetime.date.today() start_day = date - datetime.timedelta(date.weekday()) end_day = start_day + datetime.timedelta(7) return dateToDatetime([day for day in datespan(start_day, end_day)]) # 获取上月日期范围 def getLastMonth(): today = datetime.date.today() start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year, today.month - 1 if today.month - 1 != 0 else 1, 1))) end_day = start_day + datetime.timedelta(30) return [day for day in datespan(start_day, end_day)] # 获取当月日期范围 def getThisMonth(): today = datetime.date.today() start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year, today.month, 1))) end_day = start_day + datetime.timedelta(30) return [day for day in datespan(start_day, end_day)] # 获取去年日期范围 def getLastYear(): today = datetime.date.today() start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year - 1, 1, 1))) end_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year, 1, 1))) return [day for day in datespan(start_day, end_day)] # 获取当年日期范围 def getThisYear(): today = datetime.date.today() start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year, 1, 1))) end_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year + 1, 1, 1))) return [day for day in datespan(start_day, end_day)] # ========== 排除查询工具 ========== # 排除当日数据 def dayNotIn(objects): return objects.filter(~Q(date=timezone.now())) # 排除近7天数据 def weekNotIn(objects): return objects.filter(date__lt=timezone.now() - datetime.timedelta(days=7)) # 排除近30天数据 def monthNotIn(objects): return objects.filter(date__lt=timezone.now() - datetime.timedelta(days=30)) # 排除近365天数据 def yearNotIn(objects): return objects.filter(date__lt=timezone.now() - datetime.timedelta(days=365)) # ========== 按指定年月查询 ========== def getQSbyYM(objects, y, m): # m 取值范围 [0, 12] em = m + 1 ey = y if em == 13: em = 1 ey = y + 1 start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(y, m, 1))) end_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(ey, em, 1))) return objects.filter(date__lt=end_day, date__gte=start_day) # 查询当日创建的数据 def createInToday(objects): today = timezone.now() start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year, today.month, today.day))) end_day = start_day + datetime.timedelta(1) return objects.filter(date__lt=end_day, date__gte=start_day) # 日期时间转标准字符串格式 def dtstr(dt) -> str: return dt.strftime('%Y-%m-%dT%H:%M:%S') # 字符串转带时区的日期时间对象 def strdt(st): return pytz.timezone('Asia/Shanghai').localize((parse_datetime(st)))
使用说明
- 使用时请根据模型的DateTime字段名称修改
date__前缀 - 请根据站点部署的区域调整对应时区即可
内容的提问来源于stack exchange,提问作者Weilory
相关产品推荐
相关产品推荐

