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

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

Я просмотрел документация по формам Django, и в нем кратко упоминается Джанго.ВНО.администратор.виджеты, но я не знаю, как их использовать?

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

<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, поэтому, пожалуйста, полегче...

13 ответов


растущая сложность этого ответа с течением времени, и многие хаки, необходимые, вероятно, следует предостеречь вас от этого вообще. Он полагается на недокументированные внутренние детали реализации администратора, вероятно, снова сломается в будущих версиях 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.формы импорт ProductForm" вместо "от my_app.модели импортного продукта", конечно).

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

  4. и хакерская часть: виджеты даты/времени администратора предполагают, что материал i18n JS был загружен, а также требует ядра.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/ был вручную подключен к Джанго.просмотр.как 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 этого дополнение.


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


Да, я в конечном итоге переопределил/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)

начиная с Django 1.2 RC1, если вы используете трюк выбора даты администратора Django widge, следующее должно быть добавлено в ваш шаблон, или вы увидите url-адрес значка календаря, на который ссылаются через "/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>

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

(нет необходимости переопределять метод __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

ссылка Типы Полей найти значение по умолчанию поле формы.


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

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

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

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

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


Как насчет просто присвоения класса виджету, а затем привязки этого класса к jQuery datepicker?

Джанго 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();

обновленное решение и решение 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,)

форма.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'),

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

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

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

    Here is a screenshot of the Form

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

Для Django >= 2.0

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

Шаг 1: добавить javascript-catalog URL-адрес вашего проекта (не приложения) .

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