对于我的大多数应用程序,使用 TIME_ZONE 和 USE_TZ 设置保存日期时间很好。我的问题是,我如何才能完成保存日期时间为 UTC 的模型,但设置了日期时间,以便转换回用户输入的时区是正确的?模型,视图,表单和html如下所示。如果 settings.py 文件中的 USE_TZ = False,此代码将起作用,但我想保留项目中其他所有内容的时区。
型:
class TZTestModel(models.Model):
timezone = TimeZoneField()
dt = models.DateTimeField()
视图:
class TZTestView(LoginRequiredMixin, TemplateView):
template_name = "tz_test.html"
def get_context_data(self, **kwargs):
return {
'form': self.form
}
def dispatch(self, request, *args, **kwargs):
self.form = TZTestForm(self.request.POST or None)
return super(TZTestView, self).dispatch(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
if self.form.is_valid():
self.form.save()
return self.render_to_response(self.get_context_data())
形式:
class TZTestForm(forms.ModelForm):
class Meta:
model = TZTestModel
def clean(self):
timezone = self.cleaned_data['timezone']
dt = self.cleaned_data['dt']
dt = timezone.localize(dt)
self.cleaned_data['dt'] = pytz.UTC.normalize(dt.astimezone(pytz.UTC))
return self.cleaned_data
模板:
<html>
<body>
<form method="post">
{% csrf_token %}
{{ form }}
<input type="submit">
</form>
</body>
</html>
例:
我希望能够输入时区"美国/阿拉斯加"和今天的日期时间为 13:00,将其保存为 UTC 值,然后能够转换回"美国/阿拉斯加"并获得正确的值。
本质上,我正在尝试将一个模型的日期时间保存在与我的应用程序不同的时区中,其中时区由用户以与指定日期时间相同的形式指定。
我在对象级时区方面遇到了同样的问题。
我找到了这个博客条目。它并不完美,但它有效!而且不太复杂。另外处理管理员是处理的。
将片段粘贴到此处:
确保在所需的时区处理表单:
def view(request):
if request.method == 'POST':
tz_form = TimeZoneForm(request.POST)
if tz_form.is_valid():
tz = tz_form.cleaned_data['event_time_zone']
timezone.activate(tz)
# Process the full form now
else:
# assuming we have an event object already
timezone.activate(event.event_time_zone)
# Continue to create form for display on the web page
在管理员列表视图中正确显示
class EventAdmin(admin.ModelAdmin):
list_display = [..., 'event_datetime_in_timezone', ...]
def event_datetime_in_timezone(self, event):
"""Display each event time on the changelist in its own timezone"""
fmt = '%Y-%m-%d %H:%M:%S %Z'
dt = event.event_datetime.astimezone(pytz_timezone(event.event_time_zone))
return dt.strftime(fmt)
event_datetime_in_timezone.short_description = _('Event time')
在"管理员添加"视图中正确解释日期
class EventAdmin(admin.ModelAdmin):
# ...
# Override add view so we can peek at the timezone they've entered and
# set the current time zone accordingly before the form is processed
def add_view(self, request, form_url='', extra_context=None):
if request.method == 'POST':
tz_form = TimeZoneForm(request.POST)
if tz_form.is_valid():
timezone.activate(tz_form.cleaned_data['event_time_zone'])
return super(EventAdmin, self).add_view(request, form_url, extra_context)
在"管理编辑"视图中正确处理时区
class EventAdmin(admin.ModelAdmin):
# ...
# Override change view so we can peek at the timezone they've entered and
# set the current time zone accordingly before the form is processed
def change_view(self, request, object_id, form_url='', extra_context=None):
if request.method == 'POST':
tz_form = TimeZoneForm(request.POST)
if tz_form.is_valid():
timezone.activate(tz_form.cleaned_data['event_time_zone'])
else:
obj = self.get_object(request, unquote(object_id))
timezone.activate(obj.event_time_zone)
return super(EventAdmin, self).change_view(request, object_id, form_url, extra_context)
编辑:表单字段的粘贴源:http://pastebin.com/j4TnnHTS进一步讨论:https://code.djangoproject.com/ticket/21300
执行此操作的方法是创建一个返回朴素日期时间的自定义表单字段,然后将其转换为用户指定的时区,然后将其转换为 UTC。
自定义字段:
class DateTimeNoTimeZoneField(forms.DateTimeField):
def to_python(self, value):
"""
Validates that the input can be converted to a datetime. Returns a
Python datetime.datetime object.
"""
if value in validators.EMPTY_VALUES:
return None
if isinstance(value, datetime.datetime):
return value
if isinstance(value, datetime.date):
return datetime.datetime(value.year, value.month, value.day)
if isinstance(value, list):
# Input comes from a SplitDateTimeWidget, for example. So, it's two
# components: date and time.
if len(value) != 2:
raise ValidationError(self.error_messages['invalid'])
if value[0] in validators.EMPTY_VALUES and value[1] in validators.EMPTY_VALUES:
return None
value = '%s %s' % tuple(value)
# Try to coerce the value to unicode.
unicode_value = force_text(value, strings_only=True)
if isinstance(unicode_value, six.text_type):
value = unicode_value.strip()
# If unicode, try to strptime against each input format.
if isinstance(value, six.text_type):
for format in self.input_formats:
try:
return self.strptime(value, format)
except (ValueError, TypeError):
continue
raise ValidationError(self.error_messages['invalid'])
形式:
class TZTestForm(forms.ModelForm):
dt = DateTimeNoTimeZoneField()
class Meta:
model = TZTestModel
def clean(self):
tz = self.cleaned_data['timezone']
dt = self.cleaned_data['dt']
dt = pytz.UTC.normalize(tz.localize(dt).astimezone(pytz.UTC))
self.cleaned_data['dt'] = dt
return self.cleaned_data