カスタムフォームでの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を変更して、 'model':Productではなく 'form_class':ProductFormを汎用のcreate_objectビューに渡します(もちろん、「from my_app.models import Product」ではなく「from my_app.forms import ProductForm」を意味します)。

  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を使用することもできます(これについて言及してくれた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_PREFIX)が/ media / admin /にあることを意味します-設定に合わせて変更できます。理想的には、この値をハードコーディングする代わりにコンテキストプロセッサを使用してテンプレートに渡しますが、それはこの質問の範囲を超えています。

また、URL / my_admin / jsi18n /をdjango.views.i18n.javascript_catalogビュー(またはI18Nを使用していない場合はnull_javascript_catalog)に手動で接続する必要もあります。管理者がログインしているかどうかに関係なくアクセスできるように、管理アプリケーションを経由する代わりに、これを自分で行う必要があります(これを指摘してくれたJeremyに感謝します)。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
オンラインでこれを行うのに最も役立つ投稿ですが、フォームフィールドにrequired = Falseが指定されているにもかかわらず、「有効な日付/時刻を入力してください」というメッセージが表示されるため、最終的に日付/時刻ウィジェットが表示され、フィールドにデータが表示されます。空白のままにしておくと、必須ではありません。jqueryに戻ります。
PhoebeB 2009年

これはDjango 1.1.1に進む方法ですか?
ナチョ

1
Django 1.2 RC1以降では、上記に小さな変更を加える必要があります。参照:stackoverflow.com/questions/38601/…–
mshafrir

1
これがまだDjango 1.4に当てはまるかどうか知っていますか?私はjQueryの日付ピッカーを使用すると確信していますが、Djangoチームがウィジェットをより一般に利用できるようにしたのではないかと思います。(このQAが最も文書化されているようなので、私はそうは思わない)
John C

1
管理者ウィジェットを管理者の外部でより簡単に再利用できるようにする動きはありませんでしたし、そうなるとは思いません。それは大変な作業であり、取り組むべき優先度の高い項目があります。(私はDjangoコアチームの一員です)
Carl Meyer、

64

解決策はハックなので、JavaScriptで独自の日付/時刻ウィジェットを使用する方が現実的だと思います。


8
同意する。私はこれを数か月前に私のdjangoプロジェクトで試しました。私は結局あきらめて、jQueryUIで自分のものを追加しました。それを機能させるために10分のすべてを取りました。
priestc 2009年

8
記録のために、私は同意します、私はこの回答に賛成しました、そして私は実際に生産現場で私の回答でこのテクニックを使用したことはありません;-)
Carl Meyer

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

私はこの投稿を頻繁に参照していることに気づき、ドキュメントでは、デフォルトウィジェットをオーバーライドするためのややハックの少ない方法を定義していることがわかりました。

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
私はこのアプローチがそれほどハックではないことに同意しません。見た目はよくなりますが、モデルフォームによって生成されたフォームフィールドを完全に上書きしているため、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週間からこれを試していました、そしてこれは私に
たくさんの

Django 1.4以降では、MEDIA_URL名前空間全体が非推奨となり削除されたため、{{STATIC_URL}}または{%load staticfiles%}を{%static 'admin / js / core.js'%}とともに使用する必要があります。/ admin / jsi18nは、^ admin /がserver / urls.pyにマップされている場合に適切に解決されます
jeberle

Django 1.4の場合に便利なもの
Ashish

10

Django 1.2 RC1以降、Django管理者日付ピッカーウィジェットトリックを使用している場合は、以下をテンプレートに追加する必要があります。そうしないと、「/ missing-admin-media-prefix」を通じてカレンダーアイコンのURLが参照されます。 / "。

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

Carl Meyerの回答を補足するために、テンプレート内の(ヘッダー内の)有効なブロックにそのヘッダーを配置する必要があるとコメントします。

{% 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。S.

6

Django> = 2.0の場合

注:ブートストラップまたはその他のCSSフレームワークを使用している場合、管理者のスタイルシートがサイトのスタイルシートと競合する可能性があるため、日時フィールドに管理者ウィジェットを使用することはお勧めできません。ブートストラップでサイトを構築している場合は、私のbootstrap-datepickerウィジェットdjango-bootstrap-datepicker-plusを使用してください。

ステップ1:javascript-catalogプロジェクトの(アプリではない)urls.pyファイルに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,
        }

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日付ピッカーにバインドするだけではどうですか?

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


1

required = False使用してSplitDateTimeのソリューションと回避策を更新しました:

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
Sam A.

1

私はこれを使用しています。すばらしいですが、テンプレートに2つの問題があります。

  1. テンプレートに提出されたすべてのカレンダーアイコンが2回表示されます。
  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ブートストラップ: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

2020年6月3日(すべての回答が機能しませんでした。私が使用したこのソリューションを試すことができます。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()
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.