Использование виджетов времени / даты в Django в произвольной форме

171

Как я могу использовать отличные виджеты даты и времени JavaScript, которые администратор по умолчанию использует с моим настраиваемым представлением?

Я просмотрел документацию по формам Django , и в ней кратко упоминается django.contrib.admin.widgets, но я не знаю, как ее использовать?

Вот мой шаблон, к которому я хочу применить его.

<form action="." method="POST">
    <table>
        {% for f in form %}
           <tr> <td> {{ f.name }}</td> <td>{{ f }}</td> </tr>
        {% endfor %}
    </table>
    <input type="submit" name="submit" value="Add Product">
</form>

Кроме того, я думаю, что следует отметить, что я на самом деле не написал сам вид для этой формы, я использую общий вид. Вот запись из url.py:

(r'^admin/products/add/$', create_object, {'model': Product, 'post_save_redirect': ''}),

И я новичок во всем, что касается Django / MVC / MTV, поэтому, пожалуйста, будьте спокойны ...

Джош Хант
источник

Ответы:

159

Растущая сложность этого ответа с течением времени и множество необходимых взломов, вероятно, должны предостеречь вас от этого вообще. Он полагается на недокументированные внутренние детали реализации администратора, и, скорее всего, снова сломается в будущих версиях Django, и его легче реализовать, чем просто найти другой виджет календаря JS и использовать его.

Тем не менее, вот что вы должны сделать, если вы полны решимости сделать эту работу:

  1. Определите свой собственный подкласс ModelForm для вашей модели (лучше всего поместить его в forms.py в вашем приложении) и попросите его использовать AdminDateWidget / AdminTimeWidget / AdminSplitDateTime (замените «mydate» и т. Д. Соответствующими именами полей из вашей модели):

    from django import forms
    from my_app.models import Product
    from django.contrib.admin import widgets                                       
    
    class ProductForm(forms.ModelForm):
        class Meta:
            model = Product
        def __init__(self, *args, **kwargs):
            super(ProductForm, self).__init__(*args, **kwargs)
            self.fields['mydate'].widget = widgets.AdminDateWidget()
            self.fields['mytime'].widget = widgets.AdminTimeWidget()
            self.fields['mydatetime'].widget = widgets.AdminSplitDateTime()
  2. Измените ваш URLconf так, чтобы он передавал «form_class»: ProductForm вместо «model»: Product в универсальное представление create_object (это будет означать «из my_app.forms import ProductForm» вместо «из my_app.models import Product», конечно).

  3. В заголовке вашего шаблона включите {{form.media}} для вывода ссылок на файлы Javascript.

  4. И хакерская часть: административные виджеты даты / времени предполагают, что загружен материал i18n JS, а также требуют core.js, но не предоставляют ни один из них автоматически. Так что в вашем шаблоне выше {{form.media}} вам понадобится:

    <script type="text/javascript" src="/my_admin/jsi18n/"></script>
    <script type="text/javascript" src="/media/admin/js/core.js"></script>

    Вы также можете использовать следующий админ CSS (спасибо Алексу за упоминание этого):

    <link rel="stylesheet" type="text/css" href="/media/admin/css/forms.css"/>
    <link rel="stylesheet" type="text/css" href="/media/admin/css/base.css"/>
    <link rel="stylesheet" type="text/css" href="/media/admin/css/global.css"/>
    <link rel="stylesheet" type="text/css" href="/media/admin/css/widgets.css"/>

Это означает, что административный носитель Django (ADMIN_MEDIA_PREFIX) находится в / media / admin / - вы можете изменить это для своей настройки. В идеале вы должны использовать контекстный процессор для передачи этих значений в шаблон вместо жесткого кодирования, но это выходит за рамки этого вопроса.

Для этого также необходимо, чтобы URL / my_admin / jsi18n / был подключен вручную к представлению django.views.i18n.javascript_catalog (или null_javascript_catalog, если вы не используете I18N). Вы должны сделать это самостоятельно, а не просматривать приложение администратора, чтобы оно было доступно независимо от того, вошли ли вы в администратор (спасибо Джереми за то, что указал на это). Пример кода для вашего URLconf:

(r'^my_admin/jsi18n', 'django.views.i18n.javascript_catalog'),

Наконец, если вы используете Django 1.2 или более позднюю версию, вам понадобится дополнительный код в шаблоне, чтобы помочь виджетам найти свое медиа:

{% load adminmedia %} /* At the top of the template. */

/* In the head section of the template. */
<script type="text/javascript">
window.__admin_media_prefix__ = "{% filter escapejs %}{% admin_media_prefix %}{% endfilter %}";
</script>

Спасибо lupefiasco за это дополнение.

Карл Мейер
источник
3
Наиболее полезная публикация в Интернете для этого, но, наконец, после появления виджета даты / времени и заполнения поля, я собираюсь его убрать, потому что, несмотря на то, что в поле формы указано значение = False, теперь отображается сообщение «Введите правильную дату / время». для моего не обязательного поля, если я оставлю это поле пустым. Вернуться к JQuery для меня.
Фиби
Это путь к Django 1.1.1?
Начо
1
В Django 1.2 RC1 и более поздних версиях вам нужно внести небольшое изменение в вышеприведенное. См: stackoverflow.com/questions/38601/...
mshafrir
1
Вы знаете, относится ли это к Django 1.4? Несмотря на то, что я убежден в том, что нужно использовать средство выбора даты jQuery, мне любопытно, могла ли команда Django сделать свой виджет более общедоступным. (Я подозреваю, что нет, так как этот QA, кажется, больше всего документации)
Джон C
1
Не было никакого движения к тому, чтобы сделать административные виджеты более легкими для повторного использования вне администратора, и я не думаю, что они будут. Это было бы много работы, и есть гораздо более приоритетные пункты для работы. (Кстати, я часть основной команды Django).
Карл Мейер
64

Поскольку решение является хакерским, я думаю, что использование собственного виджета даты / времени с некоторым JavaScript более целесообразно.

Zgoda
источник
8
Я согласен. Я пытался сделать это несколько месяцев назад с моим проектом django. В конце концов я сдался и добавил свой собственный с jQueryUI. Потребовалось все 10 минут, чтобы все заработало.
priestc
8
Для справки, я согласен, я проголосовал за этот ответ, и я никогда не использовал технику в своем ответе на производственной площадке ;-)
Карл Мейер
12

Да, в итоге я переопределил / admin / jsi18n / url.

Вот что я добавил в своем urls.py. Убедитесь, что он выше / admin / url

    (r'^admin/jsi18n', i18n_javascript),

А вот и созданная мной функция i18n_javascript.

from django.contrib import admin
def i18n_javascript(request):
  return admin.site.i18n_javascript(request)

источник
О, очень хорошая мысль. Я добавлю это к моему ответу выше, чтобы людям было легче найти их, прежде чем они столкнутся с проблемами.
Карл Мейер
12

Я часто ссылаюсь на этот пост и обнаружил, что документация определяет немного менее хакерский способ переопределения виджетов по умолчанию.

( Нет необходимости переопределять метод __init__ в ModelForm )

Тем не менее, вам все равно нужно правильно подключить JS и CSS, как упоминает Карл.

forms.py

from django import forms
from my_app.models import Product
from django.contrib.admin import widgets                                       


class ProductForm(forms.ModelForm):
    mydate = forms.DateField(widget=widgets.AdminDateWidget)
    mytime = forms.TimeField(widget=widgets.AdminTimeWidget)
    mydatetime = forms.SplitDateTimeField(widget=widgets.AdminSplitDateTime)

    class Meta:
        model = Product

Типы справочных полей, чтобы найти поля формы по умолчанию.

monkut
источник
5
Я не согласен с тем, что такой подход менее хакерский. Конечно, это выглядит лучше, но вы полностью переопределяете поля формы, сгенерированные формой модели, что также может привести к удалению параметров из полей модели, например help_text. Переопределение init только изменяет виджет и оставляет остальную часть поля формы такой, какой она была.
Карл Мейер
11

Мой код головы для версии 1.4 (некоторые новые, а некоторые удалены)

{% block extrahead %}

<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}admin/css/forms.css"/>
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}admin/css/base.css"/>
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}admin/css/global.css"/>
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}admin/css/widgets.css"/>

<script type="text/javascript" src="/admin/jsi18n/"></script>
<script type="text/javascript" src="{{ STATIC_URL }}admin/js/core.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}admin/js/admin/RelatedObjectLookups.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}admin/js/jquery.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}admin/js/jquery.init.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}admin/js/actions.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}admin/js/calendar.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}admin/js/admin/DateTimeShortcuts.js"></script>

{% endblock %}
Romamo
источник
3
здорово . я пробовал это с прошлых 2 недель, и это помогает мне большое спасибо
нумерация
использование {{STATIC_URL}} или {% load staticfiles%} с {% static 'admin / js / core.js'%} ... требуется для Django 1.4+, поскольку все пространство имен MEDIA_URL устарело и удалено. / admin / jsi18n разрешается правильно, если у вас есть ^ admin /, сопоставленный в server / urls.py
jeberle
хороший материал, полезный в случае с Django 1.4
Ashish
10

Начиная с Django 1.2 RC1, если вы используете трюк с виджетом для выбора даты администратора Django, в шаблон необходимо добавить следующее, или вы увидите ссылку на значок календаря, на которую ссылается префикс "/ missing-admin-media-prefix /».

{% load adminmedia %} /* At the top of the template. */

/* In the head section of the template. */
<script type="text/javascript">
window.__admin_media_prefix__ = "{% filter escapejs %}{% admin_media_prefix %}{% endfilter %}";
</script>
mshafrir
источник
7

В дополнение к ответу Карла Мейера, я хотел бы прокомментировать, что вам нужно поместить этот заголовок в некоторый допустимый блок (внутри заголовка) в вашем шаблоне.

{% block extra_head %}

<link rel="stylesheet" type="text/css" href="/media/admin/css/forms.css"/>
<link rel="stylesheet" type="text/css" href="/media/admin/css/base.css"/>
<link rel="stylesheet" type="text/css" href="/media/admin/css/global.css"/>
<link rel="stylesheet" type="text/css" href="/media/admin/css/widgets.css"/>

<script type="text/javascript" src="/admin/jsi18n/"></script>
<script type="text/javascript" src="/media/admin/js/core.js"></script>
<script type="text/javascript" src="/media/admin/js/admin/RelatedObjectLookups.js"></script>

{{ form.media }}

{% endblock %}
Алекс. С.
источник
Я также нашел эту ссылку полезной: groups.google.ca/group/django-users/msg/1a40cf5f153cd23e
Алекс. С.
6

Для Джанго> = 2.0

Примечание. Использование виджетов администратора для полей даты и времени не является хорошей идеей, поскольку таблицы стилей администратора могут конфликтовать с таблицами стилей вашего сайта в случае, если вы используете загрузчик или любые другие CSS-фреймворки. Если вы создаете свой сайт с помощью bootstrap, используйте мой виджет bootstrap-datepicker django-bootstrap-datepicker-plus .

Шаг 1: Добавьте javascript-catalogURL в файл вашего проекта (не приложения) urls.py.

from django.views.i18n import JavaScriptCatalog

urlpatterns = [
    path('jsi18n', JavaScriptCatalog.as_view(), name='javascript-catalog'),
]

Шаг 2: Добавьте необходимые ресурсы JavaScript / CSS в ваш шаблон.

<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
<script type="text/javascript" src="{% static '/admin/js/core.js' %}"></script>
<link rel="stylesheet" type="text/css" href="{% static '/admin/css/widgets.css' %}">
<style>.calendar>table>caption{caption-side:unset}</style><!--caption fix for bootstrap4-->
{{ form.media }}        {# Form required JS and CSS #}

Шаг 3: Используйте виджеты администратора для полей ввода даты и времени в вашем forms.py.

from django.contrib.admin import widgets
from .models import Product

class ProductCreateForm(forms.ModelForm):
    class Meta:
        model = Product
        fields = ['name', 'publish_date', 'publish_time', 'publish_datetime']
        widgets = {
            'publish_date': widgets.AdminDateWidget,
            'publish_time': widgets.AdminTimeWidget,
            'publish_datetime': widgets.AdminSplitDateTime,
        }
Муним Манна
источник
5

Ниже также будет работать в крайнем случае, если вышеперечисленное не удалось

class PaymentsForm(forms.ModelForm):
    class Meta:
        model = Payments

    def __init__(self, *args, **kwargs):
        super(PaymentsForm, self).__init__(*args, **kwargs)
        self.fields['date'].widget = SelectDateWidget()

Такой же как

class PaymentsForm(forms.ModelForm):
    date = forms.DateField(widget=SelectDateWidget())

    class Meta:
        model = Payments

поместите это в вашем forms.py from django.forms.extras.widgets import SelectDateWidget

Денис Киоко
источник
1
это дает мне ошибку: объект DateField не имеет атрибута needs_multipart_form
madeeha ameer
3

Как насчет того, чтобы просто присвоить класс вашему виджету и затем связать этот класс с датщиком JQuery?

Джанго forms.py:

class MyForm(forms.ModelForm):

  class Meta:
    model = MyModel

  def __init__(self, *args, **kwargs):
    super(MyForm, self).__init__(*args, **kwargs)
    self.fields['my_date_field'].widget.attrs['class'] = 'datepicker'

И немного JavaScript для шаблона:

  $(".datepicker").datepicker();
trubliphone
источник
1

Обновленное решение и обходной путь для SplitDateTime с обязательным = False :

forms.py

from django import forms

class SplitDateTimeJSField(forms.SplitDateTimeField):
    def __init__(self, *args, **kwargs):
        super(SplitDateTimeJSField, self).__init__(*args, **kwargs)
        self.widget.widgets[0].attrs = {'class': 'vDateField'}
        self.widget.widgets[1].attrs = {'class': 'vTimeField'}  


class AnyFormOrModelForm(forms.Form):
    date = forms.DateField(widget=forms.TextInput(attrs={'class':'vDateField'}))
    time = forms.TimeField(widget=forms.TextInput(attrs={'class':'vTimeField'}))
    timestamp = SplitDateTimeJSField(required=False,)

form.html

<script type="text/javascript" src="/admin/jsi18n/"></script>
<script type="text/javascript" src="/admin_media/js/core.js"></script>
<script type="text/javascript" src="/admin_media/js/calendar.js"></script>
<script type="text/javascript" src="/admin_media/js/admin/DateTimeShortcuts.js"></script>

urls.py

(r'^admin/jsi18n/', 'django.views.i18n.javascript_catalog'),
Сэм А.
источник
Я также заполнил билет, чтобы исправить виджеты SplitDateTime code.djangoproject.com/ticket/12303
Сэм А.
1

Я использую это, это здорово, но у меня есть 2 проблемы с шаблоном:

  1. Я вижу значки календаря дважды для каждого файла в шаблоне.
  2. И для TimeField у меня есть « Введите действительную дату. '

    Вот скриншот формы

models.py

from django.db import models
    name=models.CharField(max_length=100)
    create_date=models.DateField(blank=True)
    start_time=models.TimeField(blank=False)
    end_time=models.TimeField(blank=False)

forms.py

from django import forms
from .models import Guide
from django.contrib.admin import widgets

class GuideForm(forms.ModelForm):
    start_time = forms.DateField(widget=widgets.AdminTimeWidget)
    end_time = forms.DateField(widget=widgets.AdminTimeWidget)
    create_date = forms.DateField(widget=widgets.AdminDateWidget)
    class Meta:
        model=Guide
        fields=['name','categorie','thumb']
Потемкин
источник
0

В Django 10. myproject / urls.py: в начале urlpatterns

  from django.views.i18n import JavaScriptCatalog

urlpatterns = [
    url(r'^jsi18n/$', JavaScriptCatalog.as_view(), name='javascript-catalog'),
.
.
.]

В моем template.html:

{% load staticfiles %}

    <script src="{% static "js/jquery-2.2.3.min.js" %}"></script>
    <script src="{% static "js/bootstrap.min.js" %}"></script>
    {# Loading internazionalization for js #}
    {% load i18n admin_modify %}
    <script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
    <script type="text/javascript" src="{% static "/admin/js/jquery.init.js" %}"></script>

    <link rel="stylesheet" type="text/css" href="{% static "/admin/css/base.css" %}">
    <link rel="stylesheet" type="text/css" href="{% static "/admin/css/forms.css" %}">
    <link rel="stylesheet" type="text/css" href="{% static "/admin/css/login.css" %}">
    <link rel="stylesheet" type="text/css" href="{% static "/admin/css/widgets.css" %}">



    <script type="text/javascript" src="{% static "/admin/js/core.js" %}"></script>
    <script type="text/javascript" src="{% static "/admin/js/SelectFilter2.js" %}"></script>
    <script type="text/javascript" src="{% static "/admin/js/admin/RelatedObjectLookups.js" %}"></script>
    <script type="text/javascript" src="{% static "/admin/js/actions.js" %}"></script>
    <script type="text/javascript" src="{% static "/admin/js/calendar.js" %}"></script>
    <script type="text/javascript" src="{% static "/admin/js/admin/DateTimeShortcuts.js" %}"></script>
Хосе Луис Кичимбо
источник
0

Настройка моего Django: 1.11 Bootstrap: 3.3.7

Так как ни один из ответов не является полностью ясным, я делюсь своим кодом шаблона, который не представляет никаких ошибок вообще.

Верхняя половина шаблона:

{% extends 'base.html' %}
{% load static %}
{% load i18n %}

{% block head %}
    <title>Add Interview</title>
{% endblock %}

{% block content %}

<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
<script type="text/javascript" src="{% static 'admin/js/core.js' %}"></script>
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/forms.css' %}"/>
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/widgets.css' %}"/>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" >
<script type="text/javascript" src="{% static 'js/jquery.js' %}"></script>

Нижняя половина:

<script type="text/javascript" src="/admin/jsi18n/"></script>
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.min.js' %}"></script>
<script type="text/javascript" src="{% static 'admin/js/jquery.init.js' %}"></script>
<script type="text/javascript" src="{% static 'admin/js/actions.min.js' %}"></script>
{% endblock %}
Ариндам Ройховдхери
источник
0

3 июня 2020 г. (Все ответы не сработали, вы можете попробовать это решение, которое я использовал. Только для TimeField)

Используйте простые Charfieldполя времени ( начало и конец в этом примере) в формах.

forms.py

мы можем использовать Formили ModelFormздесь.

class TimeSlotForm(forms.ModelForm):
    start = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'HH:MM'}))
    end = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'HH:MM'}))

    class Meta:
        model = TimeSlots
        fields = ('start', 'end', 'provider')

Преобразуйте строковый ввод во временной объект в представлениях.

import datetime
def slots():
    if request.method == 'POST':
        form = create_form(request.POST)
        if form.is_valid():                
            slot = form.save(commit=False)
            start = form.cleaned_data['start']
            end = form.cleaned_data['end']
            start = datetime.datetime.strptime(start, '%H:%M').time()
            end = datetime.datetime.strptime(end, '%H:%M').time()
            slot.start = start
            slot.end = end
            slot.save()
Сандип
источник