Використання віджетів часу / дати Django у спеціальній формі


171

Як я можу використовувати вишукані віджети дати та часу JavaScript, якими користується адміністратор за замовчуванням для мого спеціального перегляду?

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

Ось мій шаблон, на який я хочу його застосувати.

<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 для вашої моделі (найкраще помістити його у 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()
  2. Змініть свій URLconf, щоб передати "form_class": ProductForm замість "моделі": продукт до загального перегляду create_object (це означатиме, "звичайно, з my_app.forms імпортувати продуктForm", а не "з 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
Найбільш корисна публікація в Інтернеті для цього, але, нарешті, з’явився віджет дата / час та заповнення поля, я зараз його вийму, тому що, незважаючи на те, що потрібно = Неправильно у полі форми, тепер з’являється повідомлення "Введіть дійсну дату / час". для мого не обов'язкового поля, якщо я залишу його порожнім. Назад до jquery для мене.
PhoebeB

Це шлях до Django 1.1.1?
Начо

1
У Django 1.2 RC1 та пізніших версіях вам слід внести невеликі зміни до вищезазначеного. Див: stackoverflow.com/questions/38601 / ...
mshafrir

1
Чи знаєте ви, чи це все ще стосується Django 1.4? Хоча я переконаний, що йду з вибором дати jQuery, мені цікаво, якби команда Django могла зробити свій віджет більш загальнодоступним. (Я підозрюю, що це не так, оскільки ця якість якості здається найбільш документацією навколо)
Джон C

1
Не було жодного руху до того, щоб зробити віджети адміністрування більш легкими для повторного використання поза адміністратором, і я не здогадуюсь, що вони будуть такі. Було б багато роботи, і над цим є набагато більш високі пріоритетні питання. (Я є частиною основної команди Джанго, btw).
Карл Мейєр

64

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


8
Я згоден. Я спробував це зробити кілька місяців тому зі своїм проектом джанго. Зрештою я відмовився і просто додав своє власне з jQueryUI. Взяв усі 10 хвилин, щоб він працював.
священик

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 належним чином, як згадує Карл.

form.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 тижнів, і це допоможе мені дякую багато
numrah

використання {{STATIC_URL}} або {% load staticfiles%} з {% static 'admin / js / core.js'%} ... потрібно для Django 1.4+, оскільки весь простір імен MEDIA_URL був знятий і видалений. / admin / jsi18n належним чином вирішено, якщо у вас ^ admin / відображено на сервері / urls.py
jeberle

приємні речі, корисні у випадку з Django 1.4
Ashish

10

Починаючи з Django 1.2 RC1, якщо ви використовуєте трюк вибору дати адміністратора Django, до вашого шаблону потрібно додати наступне, або ви побачите, що 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>

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
Alex. С.

6

Для Джанго> = 2,0

Примітка. Використання віджетів адміністрування для полів дати-часу не є хорошою ідеєю, оскільки таблиці стилів адміністратора можуть суперечити вашим таблицям стилів веб-сайту у випадку, якщо ви використовуєте завантажувальний або будь-який інший фреймворк CSS. Якщо ви будуєте свій сайт на завантажувальному інструменті, використовуйте мій віджет 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

помістіть це у свою form.py from django.forms.extras.widgets import SelectDateWidget


1
він дає мені помилку "DateField" об'єкт не має атрибута "needs_multipart_form"
madeeha ameer

3

А що з присвоєнням класу своєму віджету, а потім прив’язуванню цього класу до вибору дати JQuery?

Джанго form.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 :

form.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)

form.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: на початку URL-шаблонів

  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 завантажувальна програма: 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для часових полів ( початок і кінець у цьому прикладі) у формах.

form.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.