【问题标题】:Accept datetime isoformat in Django form接受 Django 形式的日期时间等格式
【发布时间】:2015-04-10 07:56:19
【问题描述】:

在 Django 之上构建了一个 API 服务器。模型AuthorDateTimeField,它被序列化为iso 格式的字符串,例如2015-04-10T07:28:45.571039+00:00

class Author(models.Model):
    created = models.DateTimeField()

客户端是用 Javascript 实现的。它使用给定的日期时间字段构建模型,因此要更新模型,我想使用相同格式的字段。

要在服务器端处理请求,有一个模型表单

class AuthorForm(forms.ModelForm):
    class Meta:
        model = Author

POST/PUT 请求处理程序引发异常:“输入有效的日期/时间。”。允许这样做的原因DATETIME_INPUT_FORMATS。无法接受带有冒号的时区信息。

鉴于我不想删除时区支持(USE_TZ)并且想接受带有时区信息的数据,那么实现服务器端的方法是什么?我目前有两种解决方案:1)完全排除字段 2)在表单中使用 CharField 和表单清理方法中的 dateutil.parser.parse 函数。 您对如何处理有什么建议吗?

【问题讨论】:

    标签: python django validation datetime timezone


    【解决方案1】:

    Python(3.7 之前)无法处理带有 +00:00 的 ISO8601 字符串。具体来说,冒号是问题所在(请参阅:https://bugs.python.org/issue15873https://bugs.python.org/msg169952)。然而,Django 确实通过django.utils.dateparse.parse_datetime 提供了解决方案(参见:https://stackoverflow.com/a/30449246/5043802

    要解决此问题,您可以推出自己的 DateTimeField,改为使用 parse_datetime

    https://gist.github.com/elnygren/da7a1b7496a598f95bc8d8277f420b2d

        from django import forms
        from django.utils.dateparse import parse_datetime
        from django.utils.encoding import force_str
        from django.forms.widgets import DateTimeInput
        from django.utils.translation import gettext_lazy as _, ngettext_lazy
    
    
        class ISODateTimeField(forms.Field):
            """DateTimeField that uses django.utils.dateparse.parse_datetime.
    
            More precisely, this DateTimeField accepts ISO 8601 datetime strings 
            that specify timezone with +00:00 syntax.
    
            https://en.wikipedia.org/wiki/ISO_8601
            https://code.djangoproject.com/ticket/11385
            https://bugs.python.org/issue15873
            https://bugs.python.org/msg169952
            """
            widget = DateTimeInput
            default_error_messages = {
                'invalid': _('Enter a valid date/time.'),
            }
    
            def to_python(self, value):
                value = value.strip()
                try:
                    return self.strptime(value, format)
                except (ValueError, TypeError):
                    raise forms.ValidationError(self.error_messages['invalid'], code='invalid')
    
            def strptime(self, value, format):
                """ stackoverflow won't let me save just an indent! """
                return parse_datetime(force_str(value))
    

    【讨论】:

      【解决方案2】:

      您的存储不太可能接受时区感知日期时间,您的 django 应用程序是否设置了时区?来自文档:

      启用时区支持后,Django 存储日期时间 数据库中以 UTC 表示的信息,使用时区感知日期时间 内部对象,并将它们转换为最终用户的时区 模板和表格。

      如果你不使用 postgresql:

      其他后端存储没有时区信息的日期时间。如果你 从 USE_TZ = False 切换到 USE_TZ = True,您必须将您的 从本地时间到 UTC 的数据——如果你的本地时间是不确定的 时间有夏令时。

      基本上它还没有完全完成,你只是在 settings.py 中设置 USE_TZ

      【讨论】:

      • 我使用 Postgres 和我的设置“USE_TZ = True”,所以 django 设置了时区(否则描述中的序列化日期不会有“+00:00”后缀)。
      • 我认为存储没有问题,因为我可以通过 DATETIME_INPUT_FORMATS 设置为正确的数据复制错误以拒绝其格式。
      【解决方案3】:

      我使用@elnygren 代码,但我的表单存在一个错误,即不需要 ISODateTimeField 字段,它会一直尝试验证,我以这种方式修复:

      def to_python(self, value):
          if value in self.empty_values:
              return None
          value = value.strip()
          for _format in self.input_formats:
              try:
                  return self.strptime(value, _format)
              except (ValueError, TypeError):
                  continue
          raise forms.ValidationError(self.error_messages["invalid"], code="invalid")
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-03-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-08
        • 1970-01-01
        相关资源
        最近更新 更多