Django DateTimeInput Тип «datetime-local» не сохраняется в базе данных

Я работаю над приложением Django для документирования сделок. Вся торговая документация сделана на одной странице со встроенным набором форм, который показывает все входы и выходы для сделки. Все работает хорошо до поля datetimeinput. Если я удаляю «type», форма работает отлично, но очень не удобна для пользователя.

Рабочие записи без «типа» https://i.imgur.com/wvmD2ux.png

Неработающие записи с типом: datetime-local https://i.imgur.com/3DtbiUZ.png

Так что, я думаю, вы могли бы сказать, что существует несколько проблем, которые создает 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 %}

Всего 1 ответ


Формат, представленный полем с type=datetime-local кажется не тем, который принят Django, что в моих локальных тестах привело к тому, что форму не сохранили. Потенциально возможно добавить форматы даты и времени для полей, которые django принимает, используя DATETIME_INPUT_FORMATS в файле настроек (см. 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, чтобы принять результат в любом случае, я бы посоветовал вам рассмотреть возможность использования виджета datetime, который дает вам больший контроль и большую кросс-браузерную совместимость. Flatpickr ( https://flatpickr.js.org/ ), по-видимому, поддерживается (по состоянию на февраль 2020 года) и имеет очень ограниченные зависимости.

В вашем случае, чтобы использовать 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 не обязательно будет для вас большой проблемой.


Есть идеи?

10000