如何在 Django Forms 中使用时区
How to use timezones in Django Forms
Django 中的时区...
我不知道为什么这么难,但我很难过。
我有一个表单用用户的本地时间覆盖数据库中的 UTC 日期时间。我似乎无法弄清楚是什么原因造成的。
我的 settings.py 时区设置如下:
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'America/Toronto'
USE_I18N = True
USE_L10N = False
USE_TZ = True
我在温尼伯,我的服务器在多伦多。我的用户可以在任何地方。
我为每个用户都有一个模型字段 t_zone = models.CharField(max_length=50, default = "America/Winnipeg",)
用户可以自行更改。
关于这个模型:
class Build(models.Model):
PSScustomer = models.ForeignKey(Customer, on_delete=models.CASCADE)
buildStart = models.DateTimeField(null=True, blank=True)
...
我使用如下视图逻辑在数据库中创建了一个新条目:
...
now = timezone.now()
newBuild = Build(author=machine,
PSScustomer = userCustomer,
buildStart = now,
status = "building",
addedBy = (request.user.first_name + ' ' +request.user.last_name),
...
)
newBuild.save()
buildStart
以 UTC 格式保存到数据库中,一切正常。当我在带有 timezone.activate(pytz.timezone(self.request.user.t_zone))
的视图中更改用户的时区时,它将在他们各自的时区显示 UTC 时间。
到目前为止一切都很好(我认为)。
这是事情的发展方向:
当我希望用户更改表单中的 buildStart
时,我似乎无法获取表单以 UTC 格式将日期保存到数据库中。它将以用户选择的任何时区保存到数据库中。
使用这种形式:
class EditBuild_building(forms.ModelForm):
buildStart = forms.DateTimeField(input_formats = ['%Y-%m-%dT%H:%M'],widget = forms.DateTimeInput(attrs={'type': 'datetime-local','class': 'form-control'},format='%Y-%m-%dT%H:%M'), label = "Build Start Time")
def __init__(self, *args, **kwargs):# for ensuring fields are not left empty
super(EditBuild_building, self).__init__(*args, **kwargs)
self.fields['buildDescrip'].required = True
class Meta:
model = Build
fields = ['buildDescrip', 'buildStart','buildLength'...]
labels = {
'buildDescrip': ('Build Description'),
'buildStart': ('Build Start Time'),
...
}
widgets = {'buildDescrip': forms.TextInput(attrs={'class': 'required'}),
和这个观点:
class BuildUpdateView_Building(LoginRequiredMixin,UpdateView):
model = Build
form_class = EditBuild_building
template_name = 'build_edit_building.html'
login_url = 'login'
def get(self, request, *args, **kwargs):
proceed = True
try:
instance = Build.objects.get(id = (self.kwargs['pk']))
except:
return HttpResponse("<h2 style = 'margin:2em;'>This build is no longer available it has been deleted, please please return to dashboard</h2>")
if instance.buildActive == False:
proceed = False
if instance.deleted == True:
proceed = False
#all appears to be well, process request
if proceed == True:
form = self.form_class(instance=instance)
timezone.activate(pytz.timezone(self.request.user.t_zone))
customer = self.request.user.PSScustomer
choices = [(item.id, (str(item.first_name) + ' ' + str(item.last_name))) for item in CustomUser.objects.filter(isDevice=False, PSScustomer = customer)]
choices.insert(0, ('', 'Unconfirmed'))
form.fields['buildStrategyBy'].choices = choices
form.fields['buildProgrammedBy'].choices = choices
form.fields['operator'].choices = choices
form.fields['powder'].queryset = Powder.objects.filter(PSScustomer = customer)
context = {}
context['buildID'] = self.kwargs['pk']
context['build'] = Build.objects.get(id = (self.kwargs['pk']))
return render(request, self.template_name, {'form': form, 'context': context})
else:
return HttpResponse("<h2 style = 'margin:2em;'>This build is no longer editable here, or has been deleted, please return to dashboard</h2>")
def form_valid(self, form):
timezone.activate(pytz.timezone(self.request.user.t_zone))
proceed = True
try:
instance = Build.objects.get(id = (self.kwargs['pk']))
except:
return HttpResponse("<h2 style = 'margin:2em;'>This build is no longer available it has been deleted, please please return to dashboard</h2>")
if instance.buildActive == False:
proceed = False
if instance.deleted == True:
proceed = False
#all appears to be well, process request
if proceed == True:
form.instance.editedBy = (self.request.user.first_name)+ " " +(self.request.user.last_name)
form.instance.editedDate = timezone.now()
print('edited date ' + str(form.instance.editedDate))
form.instance.reviewed = True
next = self.request.POST['next'] #grabs prev url from form template
form.save()
build = Build.objects.get(id = self.kwargs['pk'])
if build.buildLength >0:
anticipated_end = build.buildStart + (timedelta(hours = float(build.buildLength)))
print(anticipated_end)
else:
anticipated_end = None
build.anticipatedEnd = anticipated_end
build.save()
build_thres_updater(self.kwargs['pk'])#this is function above, it updates threshold alarm counts on the build
return HttpResponseRedirect(next) #returns to this page after valid form submission
else:
return HttpResponse("<h2 style = 'margin:2em;'>This build is no longer available it has been deleted, please please return to dashboard</h2>")
当我打开这个表单时,buildStart
的日期和时间显示在我的温尼伯时区,所以 Django 从 UTC 转换为我的时区,完美,但是当我提交这个表单时,日期在DB 已从 UTC 更改为温尼伯时间。这是为什么?
我曾尝试在 form_valid
函数中将提交的时间转换为 UTC,但这似乎不是正确的方法。我在这里错过了什么?
我只想将所有时间存储为 UTC,但将它们显示在 forms/pages.
中的用户时区
编辑
当我从 get
和 form_valid
中删除 timezone.activate(pytz.timezone(self.request.user.t_zone))
时,UTC 保留在数据库中,这很棒。但是现在窗体上显示的时间是默认的TIME_ZONE
中的settings.py
。我只需要它在用户的时区....
编辑 2
我也试过补充:
{% load tz %}
{% timezone "America/Winnipeg" %}
{{form}}
{% endtimezone %}
它在表单上正确显示了时间,但是当表单提交时,它会再次从数据库中的 UTC 时间中删除 1 小时。
如果我将模板更改为:
{% load tz %}
{% timezone "Europe/Paris" %}
{{form}}
{% endtimezone %}
时间将以巴黎当地时间显示。当我提交表格时,它会以 UTC+2 将巴黎时间写入数据库。所以,总结一下:
- 创建时间记录是11:40温尼伯时间,其中写道
16:40 UTC 到数据库,完美
- 我访问表单模板,时间显示为巴黎当地时间,6:40pm,这也是我所期望的。
- 我在不更改任何字段的情况下提交表单。
- 记录已更新,时间为 22:40,即 UTC + 6 小时。
这里发生了什么!?
简单地说:您在 form_valid()
中的 activate()
调用来得太晚,无法影响表单字段,因此传入的日期时间会在默认时区中进行解释——在您的情况下是 America/Toronto
—在转换为 UTC
并保存到数据库之前。因此明显的时间偏移。
文档并没有真正指定 何时 您需要调用 activate()
。不过,据推测,它必须在 Django 将请求中的字符串值转换为表单字典中的感知 Python 日期时间之前出现(或者在发送日期时间时反之亦然)。在调用 form_valid()
时,字段值字典中已经填充了 Python 日期时间对象。
最常放置 activate()
的地方是中间件(如 this example from the documentation), since that ensures that it comes before any view processing. Alternatively, if using generic class-based views like you are, you could put it in dispatch()
.
Django 中的时区...
我不知道为什么这么难,但我很难过。 我有一个表单用用户的本地时间覆盖数据库中的 UTC 日期时间。我似乎无法弄清楚是什么原因造成的。
我的 settings.py 时区设置如下:
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'America/Toronto'
USE_I18N = True
USE_L10N = False
USE_TZ = True
我在温尼伯,我的服务器在多伦多。我的用户可以在任何地方。
我为每个用户都有一个模型字段 t_zone = models.CharField(max_length=50, default = "America/Winnipeg",)
用户可以自行更改。
关于这个模型:
class Build(models.Model):
PSScustomer = models.ForeignKey(Customer, on_delete=models.CASCADE)
buildStart = models.DateTimeField(null=True, blank=True)
...
我使用如下视图逻辑在数据库中创建了一个新条目:
...
now = timezone.now()
newBuild = Build(author=machine,
PSScustomer = userCustomer,
buildStart = now,
status = "building",
addedBy = (request.user.first_name + ' ' +request.user.last_name),
...
)
newBuild.save()
buildStart
以 UTC 格式保存到数据库中,一切正常。当我在带有 timezone.activate(pytz.timezone(self.request.user.t_zone))
的视图中更改用户的时区时,它将在他们各自的时区显示 UTC 时间。
到目前为止一切都很好(我认为)。
这是事情的发展方向:
当我希望用户更改表单中的 buildStart
时,我似乎无法获取表单以 UTC 格式将日期保存到数据库中。它将以用户选择的任何时区保存到数据库中。
使用这种形式:
class EditBuild_building(forms.ModelForm):
buildStart = forms.DateTimeField(input_formats = ['%Y-%m-%dT%H:%M'],widget = forms.DateTimeInput(attrs={'type': 'datetime-local','class': 'form-control'},format='%Y-%m-%dT%H:%M'), label = "Build Start Time")
def __init__(self, *args, **kwargs):# for ensuring fields are not left empty
super(EditBuild_building, self).__init__(*args, **kwargs)
self.fields['buildDescrip'].required = True
class Meta:
model = Build
fields = ['buildDescrip', 'buildStart','buildLength'...]
labels = {
'buildDescrip': ('Build Description'),
'buildStart': ('Build Start Time'),
...
}
widgets = {'buildDescrip': forms.TextInput(attrs={'class': 'required'}),
和这个观点:
class BuildUpdateView_Building(LoginRequiredMixin,UpdateView):
model = Build
form_class = EditBuild_building
template_name = 'build_edit_building.html'
login_url = 'login'
def get(self, request, *args, **kwargs):
proceed = True
try:
instance = Build.objects.get(id = (self.kwargs['pk']))
except:
return HttpResponse("<h2 style = 'margin:2em;'>This build is no longer available it has been deleted, please please return to dashboard</h2>")
if instance.buildActive == False:
proceed = False
if instance.deleted == True:
proceed = False
#all appears to be well, process request
if proceed == True:
form = self.form_class(instance=instance)
timezone.activate(pytz.timezone(self.request.user.t_zone))
customer = self.request.user.PSScustomer
choices = [(item.id, (str(item.first_name) + ' ' + str(item.last_name))) for item in CustomUser.objects.filter(isDevice=False, PSScustomer = customer)]
choices.insert(0, ('', 'Unconfirmed'))
form.fields['buildStrategyBy'].choices = choices
form.fields['buildProgrammedBy'].choices = choices
form.fields['operator'].choices = choices
form.fields['powder'].queryset = Powder.objects.filter(PSScustomer = customer)
context = {}
context['buildID'] = self.kwargs['pk']
context['build'] = Build.objects.get(id = (self.kwargs['pk']))
return render(request, self.template_name, {'form': form, 'context': context})
else:
return HttpResponse("<h2 style = 'margin:2em;'>This build is no longer editable here, or has been deleted, please return to dashboard</h2>")
def form_valid(self, form):
timezone.activate(pytz.timezone(self.request.user.t_zone))
proceed = True
try:
instance = Build.objects.get(id = (self.kwargs['pk']))
except:
return HttpResponse("<h2 style = 'margin:2em;'>This build is no longer available it has been deleted, please please return to dashboard</h2>")
if instance.buildActive == False:
proceed = False
if instance.deleted == True:
proceed = False
#all appears to be well, process request
if proceed == True:
form.instance.editedBy = (self.request.user.first_name)+ " " +(self.request.user.last_name)
form.instance.editedDate = timezone.now()
print('edited date ' + str(form.instance.editedDate))
form.instance.reviewed = True
next = self.request.POST['next'] #grabs prev url from form template
form.save()
build = Build.objects.get(id = self.kwargs['pk'])
if build.buildLength >0:
anticipated_end = build.buildStart + (timedelta(hours = float(build.buildLength)))
print(anticipated_end)
else:
anticipated_end = None
build.anticipatedEnd = anticipated_end
build.save()
build_thres_updater(self.kwargs['pk'])#this is function above, it updates threshold alarm counts on the build
return HttpResponseRedirect(next) #returns to this page after valid form submission
else:
return HttpResponse("<h2 style = 'margin:2em;'>This build is no longer available it has been deleted, please please return to dashboard</h2>")
当我打开这个表单时,buildStart
的日期和时间显示在我的温尼伯时区,所以 Django 从 UTC 转换为我的时区,完美,但是当我提交这个表单时,日期在DB 已从 UTC 更改为温尼伯时间。这是为什么?
我曾尝试在 form_valid
函数中将提交的时间转换为 UTC,但这似乎不是正确的方法。我在这里错过了什么?
我只想将所有时间存储为 UTC,但将它们显示在 forms/pages.
编辑
当我从 get
和 form_valid
中删除 timezone.activate(pytz.timezone(self.request.user.t_zone))
时,UTC 保留在数据库中,这很棒。但是现在窗体上显示的时间是默认的TIME_ZONE
中的settings.py
。我只需要它在用户的时区....
编辑 2
我也试过补充:
{% load tz %}
{% timezone "America/Winnipeg" %}
{{form}}
{% endtimezone %}
它在表单上正确显示了时间,但是当表单提交时,它会再次从数据库中的 UTC 时间中删除 1 小时。
如果我将模板更改为:
{% load tz %}
{% timezone "Europe/Paris" %}
{{form}}
{% endtimezone %}
时间将以巴黎当地时间显示。当我提交表格时,它会以 UTC+2 将巴黎时间写入数据库。所以,总结一下:
- 创建时间记录是11:40温尼伯时间,其中写道 16:40 UTC 到数据库,完美
- 我访问表单模板,时间显示为巴黎当地时间,6:40pm,这也是我所期望的。
- 我在不更改任何字段的情况下提交表单。
- 记录已更新,时间为 22:40,即 UTC + 6 小时。
这里发生了什么!?
简单地说:您在 form_valid()
中的 activate()
调用来得太晚,无法影响表单字段,因此传入的日期时间会在默认时区中进行解释——在您的情况下是 America/Toronto
—在转换为 UTC
并保存到数据库之前。因此明显的时间偏移。
文档并没有真正指定 何时 您需要调用 activate()
。不过,据推测,它必须在 Django 将请求中的字符串值转换为表单字典中的感知 Python 日期时间之前出现(或者在发送日期时间时反之亦然)。在调用 form_valid()
时,字段值字典中已经填充了 Python 日期时间对象。
最常放置 activate()
的地方是中间件(如 this example from the documentation), since that ensures that it comes before any view processing. Alternatively, if using generic class-based views like you are, you could put it in dispatch()
.