如何通过非字段值过滤Django QuerySet?按本地时间筛选模型
根据用户本地时间筛选Django模型记录的解决方案
我来帮你搞定这个根据用户本地时间查询的问题!首先,最优的做法是先给PersonModel添加时区字段并批量填充(你已经提到可以写迁移脚本,这步很关键,能大幅提升查询效率),然后利用数据库的时区转换函数来筛选出本地时间晚于7点的用户。
第一步:给模型添加并填充时区字段
1. 更新模型定义
先给PersonModel加上存储IANA时区(比如America/New_York)的字段:
from django.db import models class PersonModel(models.Model): zipcode = models.CharField(max_length=10) timezone = models.CharField(max_length=50, null=True, blank=True) # 新增时区字段 # 其他原有字段...
2. 生成并执行迁移
运行以下命令创建迁移并同步到数据库:
python manage.py makemigrations python manage.py migrate
3. 批量填充时区数据
写一个自定义管理命令,用你已有的邮编转时区函数批量填充:
from django.core.management.base import BaseCommand from your_app.models import PersonModel from your_utils_module import get_timezone_from_zipcode # 替换成你的时区获取函数 class Command(BaseCommand): help = 'Populate timezone field using zipcode' def handle(self, *args, **options): # 只处理还没填充时区的记录 unprocessed_persons = PersonModel.objects.filter(timezone__isnull=True) count = 0 for person in unprocessed_persons: tz = get_timezone_from_zipcode(person.zipcode) if tz: person.timezone = tz person.save() count += 1 self.stdout.write(self.style.SUCCESS(f'Successfully populated timezones for {count} records'))
把这个文件放在你的app的management/commands目录下(需要先创建这两个子目录,再加__init__.py),然后运行:
python manage.py populate_timezones # 命令名就是你这个文件的名字
第二步:编写查询语句
根据你使用的数据库类型,选择对应的查询方式:
如果你用PostgreSQL
PostgreSQL原生支持AT TIME ZONE函数,可以直接在数据库层面转换时区并提取小时:
from your_app.models import PersonModel def get_reminder_recipients(): table_name = PersonModel._meta.db_table # 用Raw SQL查询本地时间晚于19点的用户 return PersonModel.objects.raw(f""" SELECT * FROM {table_name} WHERE EXTRACT(HOUR FROM CURRENT_TIMESTAMP AT TIME ZONE timezone) >= 19 """) # 使用示例 send_reminder_query = get_reminder_recipients()
如果你用MySQL
MySQL需要用CONVERT_TZ函数,注意要确保数据库已经导入了时区表(否则CONVERT_TZ可能返回NULL):
from your_app.models import PersonModel def get_reminder_recipients(): table_name = PersonModel._meta.db_table return PersonModel.objects.raw(f""" SELECT * FROM {table_name} WHERE HOUR(CONVERT_TZ(NOW(), 'UTC', timezone)) >= 19 """) send_reminder_query = get_reminder_recipients()
关于实时用邮编查询的说明
如果暂时不想添加时区字段,你可以先批量把邮编对应的时区缓存到一个字典里,然后在Python层面过滤,但这种方式效率很低(尤其是数据量大的时候),因为需要先把所有记录拉到内存再处理:
from your_utils_module import get_timezone_from_zipcode from your_app.models import PersonModel from django.utils import timezone import pytz def get_reminder_recipients(): all_persons = PersonModel.objects.all() recipients = [] current_utc = timezone.now() for person in all_persons: tz_str = get_timezone_from_zipcode(person.zipcode) if not tz_str: continue tz = pytz.timezone(tz_str) local_time = current_utc.astimezone(tz) if local_time.hour >= 19: recipients.append(person) return recipients
强烈不推荐这种方式,数据量小的时候可以临时用,长期来看还是添加时区字段更靠谱。
内容的提问来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

