Django DateTimeInput Type 'datetime-local' Not Saving To Database

∥☆過路亽.° 提交于 2020-03-05 04:23:27

问题


I'm working on a django app to document trades. All the trade documentation is done on one page with an inline formset that shows all the entries and exits for a trade. All work well up to the datetimeinput field. If I remove the 'type' the form works great but is very non-user friendly.

Working entries with no 'type'

Non-working entries with 'type': 'datetime-local'

So I guess you could say there are several issues datetime-local creates, or maybe it's not datetime-local to blame. I've been stuck on it all day and I'm really not sure where the issue comes from:

  1. Entry.date value is not populated into field anymore
  2. When a new date and time is selected in datetimepicker it doesn't save
  3. datetime-local removes the seconds, we need the seconds

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 %}

回答1:


The format that is submitted by a field with type=datetime-local appears not to be one that is accepted by Django, which in my local tests caused the form not to save. It is potentially possible to add datetime formats for fields to those that django accepts using DATETIME_INPUT_FORMATS in the settings file (see https://docs.djangoproject.com/en/3.0/ref/settings/#std:setting-DATETIME_INPUT_FORMATS). However, if you are using localisation (i.e. USE_L10N = True in your settings) then DATETIME_INPUT_FORMATS appears not be used (based on my tests - I haven't reviewed the underlying code). In the (admittedly limited) time I spent, I could not get DATETIME_INPUT_FORMATS to have any effect at all.

Given the very limited support for datetime-local and difficulties getting django to accept the result in any case, I would suggest you consider using a datetime widget that gives you more control and greater cross-browser compatibility. One that appears (as of Feb 2020) to be maintained and have very limited dependencies is Flatpickr (https://flatpickr.js.org/).

In your case, to use Flatpickr you would need to include in top of the relevant HTML files:

<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>

and change:

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

to

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

This will cause the django-generated field to have the format that is also configured for flatpickr (which is based on the text in your fields above, and one django accepts).

There are a number of other date picker widgets you could use, and as you're already using jquery, having a jquery dependency would not necessary be a big problem for you.



来源:https://stackoverflow.com/questions/60128838/django-datetimeinput-type-datetime-local-not-saving-to-database

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!