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

StackOverflow https://stackoverflow.com/questions/38601

  •  09-06-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

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

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

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

  • В заголовке вашего шаблона добавьте {{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 для администратора (спасибо Алексу за упоминание этого):

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

    Другие советы

    Поскольку решение является хакерским, я думаю, что использование собственного виджета даты / времени с некоторым 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)
    

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

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

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

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

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

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

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

    {% 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 из django.forms.extras.widgets import SelectDateWidget

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

    Для Django > = 2.0

      

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

    Шаг 1: добавьте javascript-каталог URL в файл 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,
            }
    

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

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

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

      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 10. MyProject / urls.py: в начале urlpatterns

      from django.views.i18n import JavaScriptCatalog
    
    urlpatterns = [
        url(r'^jsi18n/
    
    

    В моем 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>
    
    , JavaScriptCatalog.as_view(), name='javascript-catalog'), . . .]

    В моем template.html:

    <*>

    Моя настройка 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 %}
    
    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top