【问题标题】:Django Import-Export DateTime Naive DateTime ErrorDjango Import-Export DateTime Naive DateTime 错误
【发布时间】:2020-11-09 10:05:40
【问题描述】:

导入日期时间错误信息:

 RuntimeWarning: DateTimeField Entry.date received a naive datetime (2020-07-20 09:23:19.881763) while time zone support is active.
  RuntimeWarning)

有没有一种方法可以让用户选择他们所在的日期时间,或者至少导入添加了 +00 的数据?目前,添加日期时间字段后,我似乎无法导入。

现在我正在尝试这个issue solution

resources.py

class TzDateTimeWidget(DateTimeWidget):

    def render(self, value, obj=None):
        if settings.USE_TZ:
            value = localtime(value)
        return super(TzDateTimeWidget, self).render(value)

class EntryResource(resources.ModelResource):
    date = fields.Field(
        column_name='date',
        attribute='datetime',
        widget=TzDateTimeWidget(format='"%Y-%m-%d %H:%M:%S"'))


    class Meta:
        model = Entry
        fields = ('date', 'entry_type', 'amount', 'price', 'fee', 'reg_fee', 'id',)
        import_order = fields
        skip_unchanged = False
        report_skipped = True

csv 文件

2020-07-17 7:42:39,,40,14.56,0,Entry,0
2020-07-17 7:47:16,,40,14.78,0,Entry,0

【问题讨论】:

    标签: django postgresql csv resources django-import-export


    【解决方案1】:

    错误在于您的对象。以下是正确的日期时间对象的外观:

    >>> from django.utils import timezone
    >>> import pytz
    >>> timezone.now()
    datetime.datetime(2013, 11, 20, 20, 8, 7, 127325, tzinfo=pytz.UTC)
    

    这是一个幼稚的对象:

    >>> from datetime import datetime
    >>> datetime.now()
    datetime.datetime(2013, 11, 20, 20, 9, 26, 423063)
    

    我假设当您尝试直接转换 csv 文件时发生错误。你应该怎么做才能分离值,将它们分配给变量,然后将它们一一添加到日期时间对象中。

    Research

    【讨论】:

    • 好的,谢谢,我会在 EntryResource 类中这样做吗?
    • 无论您在何处将数据从 csv 导入到日期时间对象中
    【解决方案2】:

    问题在于 csv 文件中的日期时间不是 Timezone aware。有几种方法可以解决这个问题。

    选项 1 - 所有用户都在一个时区

    如果您知道所提供的日期始终在您的时区中,则不需要 csv 中的时区信息,因为它将是 converted during import 并且具有正确的时区。

    选项 2 - CSV 包含 TZ 感知时间戳

    如果提供的时间戳是时区感知的,那么您可以在导入期间进行处理:

    csv:

    2020-07-20 00:00:00+03:00,40,14.56,0,Entry,0
    

    小部件:

    class TzDateTimeWidget(widgets.DateTimeWidget):
        def clean(self, value, row=None, *args, **kwargs):
            if not value:
                return None
            if isinstance(value, datetime):
                return value
            for format in self.formats:
                try:
                    if settings.USE_TZ:
                        dt = parse_datetime(value)
                    return dt
                except (ValueError, TypeError):
                    continue
            raise ValueError("Enter a valid date/time.")
    

    选项 3 - 为每个用户存储时区并在导入期间应用它

    如果您的用户可以在任何时区,但不可能在 csv 中编码 tz,那么您可以存储用户的时区(例如在自定义用户模型实例中),并在导入期间为用户应用转换.

    这个的简化版本是:

    class UserDateTimeWidget(widgets.DateTimeWidget):
        def __init__(self, user_tz_dict):
            """pass in a dict mapping user_id to timezone"""
            super().__init__()
            self.user_dict = user_tz_dict
    
        def clean(self, value, row=None, *args, **kwargs):
            dt = super().clean(value, row, args, kwargs)
            # the row will need to contain a reference to the user id
            user_id = row.get("user_id")
            user_tz = self.user_dict.get(user_id)
            return make_aware(dt, timezone=user_tz)
    

    请务必注意,这依赖于 csv 时间戳采用一致的标准,例如 UTC。

    请注意,还有其他方法可以实现上述目的,例如覆盖 Model save() 方法以在保存实例时应用用户的时区。显然,您必须将用户链接到导入的对象。

    附带说明,您引用的TzDateTimeWidget 只会在导出数据时在定义的时区中格式化日期。 DateTimeWidget 中已经存在此功能。

    【讨论】:

    • 感谢 Matthew 非常详细的回复!仍在努力。我喜欢不同的选项,实际上尝试了选项 2,但有一些问题。自动将 +00 添加到所有日期时间的末尾。应该再试一次。理想的解决方案是选项 3。如果我将用户 tz 保存在他们的个人资料中(如下所示:stackoverflow.com/a/45867250/8315752),我应该在哪里调用它?我不确定我是否完全理解 user_tz_dict,当设置中的 USE_TZ = True 时,这是 django 用户模型的自定义字段还是默认字段?
    • 我在所有测试中遇到的一个非常常见的问题是,尽管 csv 的所有字段都正确填写了,但导入了 balnk 数据。这里没有错误消息,这使得调试变得困难,但这里是管理员导入确认页面的屏幕截图:i.imgur.com/dRjhRZ4.png
    • 如果您愿意,很高兴与您交流:chat.stackoverflow.com/rooms/218260/django-import-export
    • 目前的解决方案目前一直在设置中注释掉 USE_TZ = True,看起来并不完美,但向前迈出了一步。查看上面的聊天链接了解更多信息。
    • USE_TZ 应该没问题,并为您处理日期时间转换。我建议设置一个调试器,看看你是否能看到发生了什么。
    【解决方案3】:

    如果您使用 django-import-export 导出数据并且日期时间不支持导出时区,则只需在设置中更改日期时间输入格式:

    DATETIME_INPUT_FORMATS = ("%Y-%m-%dT%H:%M:%S%Z",)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-31
      • 2017-07-16
      • 1970-01-01
      • 2019-12-26
      • 2021-11-14
      • 1970-01-01
      • 1970-01-01
      • 2014-01-08
      相关资源
      最近更新 更多