Django 管理中带有字段特定时区的 DateTimeField

DateTimeField with field specific timezone in Django admin

我正在开发一个基本的 django 应用程序,只是想不出一种方法来实现所请求的功能。

我有一个 Campaign 模型通过 1-n 关系链接到 Alert 模型。 Alert 有一个 DateTimeField(代表配置的警报时间)。但是,每个警报都应该能够针对特定时区进行设置。

我试图覆盖保存功能以应用特定于模型的时区实例,但我无法让它以令人满意的方式工作。保存后显示时将使用全球时区。

简化代码:

import pytz

from django.db import models
from django.conf import settings
from timezone_field import TimeZoneField

class Campaign(models.Model):
    name = models.CharField(max_length=80, unique=True)

class Alert(models.Model):
    campaign = models.ForeignKey(Campaign, on_delete=models.CASCADE,
            related_name='alerts')
    timezone = TimeZoneField(default=settings.TIME_ZONE)
    run_at = models.DateTimeField()

    def save(self, *args, **kwargs):
        tz = pytz.timezone(str(self.timezone))
        print("Converting", self.run_at)
        self.run_at = self.run_at.replace(tzinfo=None)
        self.run_at = tz.localize(self.run_at)
        print("to", self.run_at)
        super().save(*args, **kwargs)

如果有人对如何使其正常工作有任何建议,我将不胜感激。或者指导我安装一个插件来解决它(我找不到)。

编辑: 使用 Django 3.0.7、PostgreSQL 数据库构建。数据库中的记录生成为带有 TZ 的时间戳,因此我非常有信心 Django ORM 将这些记录视为时区感知记录。 我试图让应用程序在 Django 管理员中以自己的时区呈现警报日期。 因此,如果您有 10:00 纽约和 18:00 首尔,我希望 UI 准确显示这两个 date/times 在各自时区的情况。

区分原始和感知日期时间对象之间的区别以及默认时区和当前时区之间的区别很重要。假设您使用的是 Django 3.1,文档 here 对此进行了很好的解释。根据我在 Django 中存储日期时间的经验,最好以 UTC 格式存储它们,并允许 Django 在模板中呈现日期时间。在那些关于选择当前时区的文档中有一个很好的解释。

很难说你的具体情况,但我猜你传递的是一个天真的日期时间对象,而 Django 正在将时区设置为默认时区。您可以使用 is_awareis_naive 函数检查 datetime 对象的原始性。分享更多信息也可能会有所帮助,例如您正在使用的 Django 版本和 settings.TIME_ZONE.

的值

正如 documentation 中所说:

When support for time zones is enabled, Django stores datetime information in UTC in the database, uses time-zone-aware datetime objects internally, and translates them to the end user’s time zone in templates and forms.

因此,尝试将 datetime 转换为 save() 毫无意义,因为它将始终转换回 UTC 进行存储。显示转换 datetime 的时间。通常整个响应的时区是相同的,所以你只需 activate() 中间件或视图中的那个时区。

在您的情况下,听起来您想在管理员中显示每个 Alert 具有自己的本地化时间。有很多方法可以处理这个问题。您可以创建一个 属性 来计算本地化时间并在管理员中使用它,或者简单地预处理每个 Alert 以本地化时间。但关键是你在显示值时这样做,而不是保存它。