【问题标题】:Django DateTimeInput Type 'datetime-local' Not Saving To DatabaseDjango DateTimeInput 类型'datetime-local'不保存到数据库
【发布时间】:2020-05-24 11:58:02
【问题描述】:

我正在开发一个 django 应用程序来记录交易。所有交易文件都在一个页面上完成,其中包含一个内联表单集,其中显示了交易的所有进入和退出。在 datetimeinput 字段之前,一切都很好。如果我删除“类型”,表单效果很好,但对用户非常不友好。

没有“类型”的工作条目

'type' 的非工作条目:'datetime-local'

所以我想您可能会说 datetime-local 造成了几个问题,或者可能不是 datetime-local 的责任。我整天都卡在它上面,我真的不确定问题出在哪里:

  1. Entry.date 值不再填充到字段中
  2. 在 datetimepicker 中选择新的日期和时间时,它不会保存
  3. datetime-local 删除秒,我们需要秒

models.py

from django.db.models.functions import datetime

class Entry(models.Model):
    ENTRY = 'entry'
    EXIT = 'exit'

    ENTRY_TYPE_CHOICES = [
        (ENTRY, 'Entry'),
        (EXIT, 'Exit'),
    ]

    class Meta:
        verbose_name = "Entry"
        verbose_name_plural = "Entries"

    trade = models.ForeignKey(Trade, on_delete=models.CASCADE)
    date = models.DateTimeField(null=True, blank=True, default=datetime.datetime.now)
    amount = models.FloatField(null=True)
    price = models.FloatField(null=True, blank=True)
    fee = models.FloatField(null=True, blank=True)
    entry_type = models.CharField(max_length=5, choices=ENTRY_TYPE_CHOICES, default=ENTRY)

forms.py

class EntriesForm(ModelForm):
    class Meta:
        model = Entry
        exclude = ()
        widgets = {
            'date': forms.DateTimeInput(attrs={'type':'datetime-local', 'class':'form-control'}),
        }

EntriesFormSet = inlineformset_factory(Trade, Entry, form=EntriesForm, extra=1)

views.py

class TradeUpdateView(UpdateView):
    model = Trade
    form_class = CreateForm
    success_url = reverse_lazy('trade-list')

    def get_context_data(self, **kwargs):
        data = super(TradeUpdateView, self).get_context_data(**kwargs)
        if self.request.POST:
            data['entries'] = EntriesFormSet(self.request.POST, instance=self.object)
        else:
            data['entries'] = EntriesFormSet(instance=self.object)
        return data

    def form_valid(self, form):
        form.instance.user = self.request.user
        form.instance.created_by = self.request.user
        context = self.get_context_data()
        entries = context['entries']
        with transaction.atomic():
            self.object = form.save()

            if entries.is_valid():
                entries.instance = self.object
                entries.save()
        return super(TradeUpdateView, self).form_valid(form)

trade_form.html

{% extends 'dashboard/base.html' %}
{% load static %}

{% block content %}

<script type="text/javascript" src="{% static 'vendor/jquery/jquery.js' %}"></script>
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>

<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-3">
    <h2>New Trade</h2>
</div>

<div class="row">
    <div class="col">
        <form action="" method="post">{% csrf_token %}
            {{ form.as_p }}

            <table class="table">
                {{ entries.management_form }}

                {% for form in entries.forms %}
                {% if forloop.first %}
                <thead>
                <tr>
                    {% for field in form.visible_fields %}
                    <th>{{ field.label|capfirst }}</th>
                    {% endfor %}
                </tr>
                </thead>
                {% endif %}
                <tr class="{% cycle row1 row2 %} formset_row">
                    {% for field in form.visible_fields %}
                    <td>
                        {# Include the hidden fields in the form #}
                        {% if forloop.first %}
                        {% for hidden in form.hidden_fields %}
                        {{ hidden }}
                        {% endfor %}
                        {% endif %}
                        {{ field.errors.as_ul }}
                        {{ field }}
                    </td>
                    {% endfor %}
                </tr>
                {% endfor %}
            </table>
            <input type="submit" value="Save"/> <a href="{% url 'trade-list' %}">back to the list</a>
        </form>
    </div>


</div>


<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="{% static 'js/formset/jquery.formset.js' %}"></script>
<script type="text/javascript">
    $('.formset_row').formset({
        addText: 'add entry',
        deleteText: 'remove',
        prefix: 'entry_set'
    });

</script>


{% endblock content %}

【问题讨论】:

    标签: python django datetime django-forms datetimepicker


    【解决方案1】:

    type=datetime-local 字段提交的格式似乎不是 Django 接受的格式,这在我的本地测试中导致表单无法保存。可以在设置文件中使用DATETIME_INPUT_FORMATS 将字段的日期时间格式添加到django 接受的字段中(请参阅https://docs.djangoproject.com/en/3.0/ref/settings/#std:setting-DATETIME_INPUT_FORMATS)。但是,如果您使用本地化(即设置中的 USE_L10N = True),则 DATETIME_INPUT_FORMATS 似乎没有被使用(基于我的测试 - 我没有查看底层代码)。在我花费的(诚然有限的)时间里,我根本无法让 DATETIME_INPUT_FORMATS 产生任何效果。

    鉴于对datetime-local 的支持非常有限,并且无论如何都难以让 django 接受结果,我建议您考虑使用日期时间小部件,它可以为您提供更多控制和更好的跨浏览器兼容性。 Flatpickr (https://flatpickr.js.org/) 似乎需要维护(截至 2020 年 2 月)且依赖项非常有限。

    在您的情况下,要使用 Flatpickr,您需要在相关 HTML 文件的顶部包含:

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
    <script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
    
    
    <script>
        // This code activates flatpickr on fields with the 'datetimefield' class when the document has loaded
        window.addEventListener("DOMContentLoaded", function () {
            flatpickr(".datetimefield", {
                enableTime: true,
                enableSeconds: true,
                dateFormat: "Y-m-d H:i:S",
            });
        });
    </script>
    
    

    并改变:

            widgets = {
                'date': forms.DateTimeInput(attrs={'type':'datetime-local', 'class':'form-control'}),
            }
    

            widgets = {
                'date': forms.DateTimeInput(format='%Y-%m-%d %H:%M:%S', attrs={'class':'datetimefield'}),
            }
    

    这将导致 django 生成的字段具有也为 flatpickr 配置的格式(基于您上面字段中的文本,并且一个 django 接受)。

    您可以使用许多其他日期选择器小部件,并且由于您已经在使用 jquery,因此拥有 jquery 依赖项对您来说不是一个大问题。

    【讨论】:

    • 感谢 SimeonJM! flatpickr 包 def 解决了这个问题。我不知道 datetime-local 和 django 有问题。起初,我安装了软件包 pip 并按照他们的文档进行操作,但没有成功。然后我尝试了你的脚本解决方案,它奏效了。还不完全确定为什么。我仍然遇到问题的一件事是获取新行来显示 flatpickr 小部件。目前,它仅适用于已经存在的行/对象。日期时间字段是类而不是类型是否有原因?
    • 在我上一条评论中意识到,我并没有真正强调点击“添加条目”后的新条目/行没有显示 datetimeinput 小部件的事实。我相信这可能是一个脚本问题。你有什么推荐的吗?一直在尝试这个答案,但没有运气:stackoverflow.com/questions/6719063/…
    • @AlexWinkler 之所以使用一个类是因为 flatpickr '.datetimefield' 的第一个参数的意思是“将 flatpickr 添加到所有具有 datetimefield 类的字段中”。
    • @AlexWinkler 如果添加其他字段,则需要为新字段再次运行“flatpickr”。如果您使用已知 id 创建字段,则可以运行 flatpickr("#id-of-new-field", {...}) (其中...是原始示例中的参数)。 (我正在打电话,所以希望没有错过任何事情)。
    • 几乎,但这里有一张图片来显示我的问题i.imgur.com/FKQC1iu.png - 基本上当我单击“添加条目”时,新条目行不再具有 datetimepicker 小部件。这很奇怪,因为它有正确的类。 +1 字段代表 django 表单自动添加的附加字段。这似乎工作正常。只是后面添加的字段。
    【解决方案2】:

    可能有点晚了,但我遇到了类似的问题,我正在使用模型表单,并且 Django 在 html 表单元素中设置了无效的日期时间值(至少对于 Chrome 而言)。看看你的是否也在做同样的事情 - 查看源代码。

    解决方法是设置日期格式,以便在 html 元素的日期和时间组件之间插入一个 T:

    widgets = {
      # NOTE the form element needs "2021-01-01T12:00:00" BUT by default it's like: "2021-01-01 12:00:00"
      'start_time': DateTimeInput(format='%Y-%m-%dT%H:%M:%S', attrs={'type': 'datetime-local'}), 
      'end_time': DateTimeInput(format='%Y-%m-%dT%H:%M:%S', attrs={'type': 'datetime-local'}),
    }
    

    顺便说一句,现在建议使用 datetime-local 而不是 datetime(已弃用):mozilla page

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-07-11
      • 2018-07-07
      • 1970-01-01
      • 2021-01-02
      • 1970-01-01
      • 2017-09-04
      相关资源
      最近更新 更多