Использование виджетов времени / даты в 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 более целесообразно.


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

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


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

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>

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


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()
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.