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

Как я могу использовать яркие виджеты даты и времени 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, поэтому, пожалуйста, пройдите...

Ответ 1

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

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

  • Определите свой собственный подкласс ModelForm для вашей модели (лучше всего поместить его в form.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()
    
  • Измените свой URLconf, чтобы передать 'form_class': ProductForm вместо 'model': Продукт для общего представления create_object (это будет означать "из my_app.forms import ProductForm" вместо "from my_app.models import Product", конечно).

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

  • И хакерская часть: виджеты даты/времени администратора предполагают, что материал 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 (спасибо Alex за это):

    <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 (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 за это дополнение.

Ответ 2

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

Ответ 3

Да, я закончил переопределять /admin/jsi 18n/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)

Ответ 4

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

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

Однако вам все равно нужно подключить 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

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

Ответ 5

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

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

Ответ 6

Мой головной код для версии 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 %}

Ответ 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 %}

Ответ 8

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

Что случилось с DRY? Я думаю, что было бы лучше повторно использовать виджет администратора, но, возможно, он должен быть отделен от администратора и проще в использовании. Спасибо за эту информацию в любом случае.

Ответ 9

Мне наконец-то удалось заставить этот виджет работать на dev-сервере, только чтобы он сломался при развертывании. Я, наконец, решил, что это не стоит делать shoehorning на мой сайт, и написал мой собственный виджет. Это не так гибко, но, вероятно, это будет хорошо работать для многих: http://www.copiesofcopies.org/webl/?p=81

Ответ 10

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

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

поместите это в свои формы .py from django.forms.extras.widgets import SelectDateWidget

Ответ 11

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

Django 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();

Ответ 12

Обновленное решение и обходной путь для SplitDateTime с required = 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'),

Ответ 13

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

  from django.views.i18n import JavaScriptCatalog

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

В моем шаблоне .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>