首页 > 解决方案 > 与 Django 时间字段交织在一起的问题迫使黑客编辑/显示时间

问题描述

姜戈 2

这里有很多信息。我将尝试总结问题的本质。

背景事实:为时间字段设置默认值需要在模型参考中设置 datetime.time:https ://code.djangoproject.com/ticket/6754

这里有两个相关的问题。

问题一:时间字段的数据类型发生变化

第一个问题是下面 Django 页面的初始 GET 返回一个数据类型为<class 'datetime.time'>. 这是好的和预期的。

但是,如果您通过 POST 提交该表单,并且存在字段验证错误,则时间字段的数据类型会更改为字符串。查看此问题底部的服务器日志以查看。结果,数据的格式也发生了变化——注意秒数被丢弃了。这也可以在下面的服务器日志中看到。

这导致问题2:

问题 2:当传入的数据没有秒数时,Django 的时间格式化标签返回 None。

在下面的 HTML 模板中,您可以看到包含以下内容的行:

time:'H:i'

这是一个格式化标签,它接受一个时间,它应该以 hh:mm 格式输出时间。如果输入的格式为 18:00:00,则它可以工作。如果输入的格式为 18:00,则返回 None

这个的用户体验:

所以发生的情况是用户最初通过 GET 加载表单,默认时间以 HH:MM 格式显示 OK。用户填写一些字段并提交,如果出现验证错误,则再次显示页面,并显示验证错误消息,但现在时间已经消失,因为它将数据类型更改为字符串,从而减少了秒数,现在没有时间显示,因为如果 Django 模板时间过滤器标签的输入没有秒,则返回 None。

问题 1: 如何解决这个问题,而无需通过我转换为字符串的附加字段来强制时间字段。这行得通,但这只是解决上述问题的一种技巧。

问题 2: 为什么在初始 GET 表单加载后 POST 到服务器时,时间字段的类型从 datetime.time 变为 string?

问题3: 为什么Django模板时间标签转换在其输入不包含秒的情况下返回None?

Django HTML 模板:

<form action="{% url 'main:testtime' %}" method="post">

    <br/><br/>
    Event title<br/>
    {% if form.title.errors %}
        {% for error in form.title.errors %}
            <div class="form-error">{{ error|escape }}</div>
        {% endfor %}
    {% endif %}
    <input type="text"
           name="title"
           id="id_title"
    >

    <br/><br/>
    Event time<br/>
    {{ form.time.value|time:'H:i' }}<br/>
    {{ form.time.value }}<br/>
    <input
            type="time"
            name="time"
            required
            step="60"
            id="id_time"
            value="{{ form.time.value|time:'H:i' }}"
    >

    <br/><br/>
    {% csrf_token %}

    <input type="submit" value="submit">
</form>

Django形式:

class TestForm(forms.ModelForm):

    class Meta:
        model = Event

        fields = [
            'date',
            'time',
            'title',
        ]

    def clean_title(self):
        raise forms.ValidationError('This forces a valdation error')

Django 模型(删除了一堆不相关的字段)

class Event(models.Model):
    @staticmethod
    def default_datetime():
        return datetime.time(18, 00, 00)

    date = models.DateField(blank=False, null=False)
    time = models.TimeField(default=default_datetime.__func__, blank=False, null=False)
    title = models.TextField()

来自 urls.py 的相关行

path('testtime/', views.testtime, name='testtime'),

来自views.py的相关函数

@require_http_methods(['GET', 'POST'])
@login_required
def testtime(request):
    if request.method == 'POST':
        form = TestForm(request.POST)
        print("POST HANDLE NEW EVENT")

    elif request.method == 'GET':
        form = TestForm()
        print("GET NEW EVENT")

    print("type of form['time'].value() is:")
    print(type(form['time'].value()))
    print("value of form['time'].value() is:")
    print(form['time'].value())

    return render(request, 'main/testtime.html', {
        'form': form,
    })

服务器日志:

Jan 22 21:39:22 ip-10-0-1-251 uwsgi[14637]: GET NEW EVENT
Jan 22 21:39:22 ip-10-0-1-251 uwsgi[14637]: type of form['time'].value() is:
Jan 22 21:39:22 ip-10-0-1-251 uwsgi[14637]: <class 'datetime.time'>
Jan 22 21:39:22 ip-10-0-1-251 uwsgi[14637]: value of form['time'].value() is:
Jan 22 21:39:22 ip-10-0-1-251 uwsgi[14637]: 18:00:00
Jan 22 21:39:22 ip-10-0-1-251 uwsgi[14637]: [pid: 14647|app: 0|req: 1/5] xx.xx.102.186 () {44 vars in 1026 bytes} [Tue Jan 22 21:39:22 2019] GET /testtime/ => generated 580 bytes in 108 msecs (HTTP/1.1 200) 5 headers in 293 bytes (2 switches on core 0)



Jan 22 21:39:37 ip-10-0-1-251 uwsgi[14637]: POST HANDLE NEW EVENT
Jan 22 21:39:37 ip-10-0-1-251 uwsgi[14637]: type of form['time'].value() is:
Jan 22 21:39:37 ip-10-0-1-251 uwsgi[14637]: <class 'str'>
Jan 22 21:39:37 ip-10-0-1-251 uwsgi[14637]: value of form['time'].value() is:
Jan 22 21:39:37 ip-10-0-1-251 uwsgi[14637]: 20:03
Jan 22 21:39:37 ip-10-0-1-251 uwsgi[14637]: [pid: 14645|app: 0|req: 3/6] xx.xx.102.186 () {54 vars in 1263 bytes} [Tue Jan 22 21:39:37 2019] POST /testtime/ => generated 658 bytes in 8 msecs (HTTP/1.1 200) 5 headers in 293 bytes (2 switches on core 0)

标签: django

解决方案


问题 1:如何解决这个问题,而无需通过我转换为字符串的附加字段来强制时间字段。这行得通,但这只是解决上述问题的一种技巧。

您应该使用模板中的表单字段来解决这个问题。而不是

<input
        type="time"
        name="time"
        required
        step="60"
        id="id_time"
        value="{{ form.time.value|time:'H:i' }}"
>

利用:

 {{ form.time }}

如果需要修改属性,可以通过表单类定义进行。

问题 2:为什么在初始 GET 表单加载后 POST 到服务器时,时间字段的类型从 datetime.time 变为 string?

在 GET 流中,值设置为默认值,即 python 时间对象。对于 POST 流,值设置为request.POST作为字符串传入的值。如果你要运行,.is_valid()那么检查form.cleaned_data['time']它是否是datetime.time.

问题3:为什么Django模板时间标签转换在其输入不包含秒的情况下返回None?

(我认为)这不是因为它没有任何秒数,而是因为它所应用的值是一个字符串。如果你使用cleaned_data它会正确渲染。cleaned_data但是,这意味着当它是 GET/POST 或检查表单以查看是否已设置时,您将需要 if/else 来处理。


推荐阅读