Menggunakan widget waktu / tanggal Django dalam bentuk khusus

171

Bagaimana saya bisa menggunakan widget tanggal dan waktu JavaScript yang bagus yang digunakan admin default dengan tampilan kustom saya?

Saya telah membaca dokumentasi formulir Django , dan secara singkat menyebutkan django.contrib.admin.widgets, tetapi saya tidak tahu bagaimana menggunakannya?

Inilah templat yang saya inginkan.

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

Juga, saya pikir perlu dicatat bahwa saya belum benar-benar menulis tampilan sendiri untuk formulir ini, saya menggunakan tampilan generik. Ini adalah entri dari url.py:

(r'^admin/products/add/$', create_object, {'model': Product, 'post_save_redirect': ''}),

Dan saya relevan untuk semua hal Django / MVC / MTV, jadi silakan pergi ...

Josh Hunt
sumber

Jawaban:

159

Semakin kompleksnya jawaban ini dari waktu ke waktu, dan banyak peretasan diperlukan, mungkin harus mengingatkan Anda untuk tidak melakukan ini sama sekali. Ini bergantung pada detail implementasi internal admin yang tidak berdokumen, kemungkinan akan pecah lagi di versi Django yang akan datang, dan tidak lebih mudah diimplementasikan daripada hanya menemukan widget kalender JS lain dan menggunakannya.

Yang mengatakan, inilah yang harus Anda lakukan jika Anda bertekad untuk membuat ini berfungsi:

  1. Tentukan subclass ModelForm Anda sendiri untuk model Anda (terbaik untuk meletakkannya di forms.py di aplikasi Anda), dan katakan untuk menggunakan AdminDateWidget / AdminTimeWidget / AdminSplitDateTime (ganti 'mydate' dll dengan nama bidang yang tepat dari model Anda):

    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. Ubah URLconf Anda untuk lulus 'form_class': ProductForm, bukan 'model': Produk ke tampilan create_object generik (itu berarti "dari my_app.forms impor ProductForm", bukan "dari my_app.models import Product", tentu saja).

  3. Di bagian atas template Anda, sertakan {{form.media}} untuk menampilkan tautan ke file Javascript.

  4. Dan bagian hacky: widget admin tanggal / waktu menganggap bahwa hal-hal JS i18n telah dimuat, dan juga memerlukan core.js, tetapi tidak menyediakan salah satu secara otomatis. Jadi dalam templat Anda di atas {{form.media}} Anda akan memerlukan:

    <script type="text/javascript" src="/my_admin/jsi18n/"></script>
    <script type="text/javascript" src="/media/admin/js/core.js"></script>

    Anda mungkin juga ingin menggunakan admin CSS berikut (terima kasih Alex karena menyebutkan ini):

    <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"/>

Ini menyiratkan bahwa media admin Django (ADMIN_MEDIA_PREFIX) ada di / media / admin / - Anda dapat mengubahnya untuk pengaturan Anda. Idealnya Anda akan menggunakan pengolah konteks untuk meneruskan nilai-nilai ini ke template Anda alih-alih meng-hardcode-nya, tapi itu di luar cakupan pertanyaan ini.

Ini juga mensyaratkan bahwa URL / my_admin / jsi18n / secara manual ditransfer ke tampilan django.views.i18n.javascript_catalog (atau null_javascript_catalog jika Anda tidak menggunakan I18N). Anda harus melakukan ini sendiri alih-alih melalui aplikasi admin sehingga dapat diakses terlepas dari apakah Anda masuk ke admin (terima kasih Jeremy karena menunjukkan ini). Kode contoh untuk URLconf Anda:

(r'^my_admin/jsi18n', 'django.views.i18n.javascript_catalog'),

Terakhir, jika Anda menggunakan Django 1.2 atau lebih baru, Anda memerlukan beberapa kode tambahan dalam templat Anda untuk membantu widget menemukan medianya:

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

Terima kasih lupefiasco untuk penambahan ini.

Carl Meyer
sumber
3
Posting paling membantu online untuk melakukan ini tetapi akhirnya memiliki widget tanggal / waktu muncul dan mengisi bidang sekarang saya akan mengambilnya karena meskipun telah diperlukan = Salah di bidang formulir sekarang menunjukkan pesan "Masukkan tanggal / waktu yang valid." untuk bidang yang tidak wajib saya jika saya biarkan kosong. Kembali ke jquery untukku.
PhoebeB
Apakah ini cara untuk menggunakan Django 1.1.1?
Nacho
1
Di Django 1.2 RC1 dan yang lebih baru, Anda perlu membuat perubahan kecil ke atas. Lihat: stackoverflow.com/questions/38601/…
mshafrir
1
Apakah Anda tahu jika ini masih berlaku untuk Django 1.4? Meskipun saya yakin untuk pergi dengan datepicker jQuery, saya ingin tahu apakah tim Django mungkin membuat widget mereka lebih tersedia untuk umum. (Saya kira tidak, karena QA ini tampaknya menjadi dokumentasi paling banyak di sekitar)
John C
1
Belum ada gerakan untuk membuat widget admin lebih mudah digunakan kembali di luar admin, dan saya tidak akan menebak bahwa akan ada. Itu akan banyak pekerjaan, dan ada item prioritas yang lebih tinggi untuk dikerjakan. (Saya bagian dari tim inti Django, btw).
Carl Meyer
64

Karena solusinya adalah meretas, saya pikir menggunakan widget tanggal / waktu Anda sendiri dengan beberapa JavaScript lebih layak.

zgoda
sumber
8
Saya setuju. Saya mencoba melakukan ini beberapa bulan yang lalu dengan proyek Django saya. Saya akhirnya menyerah dan hanya menambahkan saya sendiri dengan jQueryUI. Butuh waktu 10 menit untuk membuatnya bekerja.
priestc
8
Sebagai catatan, saya setuju, saya telah mengangkat jawaban ini, dan saya tidak pernah benar-benar menggunakan teknik ini dalam jawaban saya di lokasi produksi ;-)
Carl Meyer
12

Yap, saya akhirnya mengganti / admin / jsi18n / url.

Inilah yang saya tambahkan di urls.py saya. Pastikan itu di atas / admin / url

    (r'^admin/jsi18n', i18n_javascript),

Dan inilah fungsi i18n_javascript yang saya buat.

from django.contrib import admin
def i18n_javascript(request):
  return admin.site.i18n_javascript(request)

sumber
Oh, poin yang sangat bagus. Saya akan menambahkan ini ke jawaban saya di atas sehingga lebih mudah bagi orang untuk menemukan sebelum mereka mengalami masalah.
Carl Meyer
12

Saya menemukan diri saya banyak merujuk posting ini, dan menemukan bahwa dokumentasi mendefinisikan cara yang sedikit kurang hacky untuk menimpa widget default.

( Tidak perlu mengganti metode __init__ ModelForm )

Namun, Anda masih perlu menghubungkan JS dan CSS Anda dengan tepat seperti yang disebutkan Carl.

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

Jenis Bidang Referensi untuk menemukan bidang formulir default.

monkut
sumber
5
Saya tidak setuju bahwa pendekatan ini tidak terlalu meretas. Ini terlihat lebih bagus, tentu saja, tetapi Anda benar-benar mengesampingkan bidang formulir yang dihasilkan oleh formulir model, yang juga bisa menghapus opsi dari bidang model, seperti help_text. Mengesampingkan init hanya mengubah widget dan membiarkan bidang isian lainnya seperti sebelumnya.
Carl Meyer
11

Kode kepala saya untuk versi 1.4 (beberapa baru dan beberapa dihapus)

{% 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 %}
Romamo
sumber
3
luar biasa. Saya sudah mencoba ini dari 2 minggu terakhir dan ini membantu saya terima kasih banyak
numerah
menggunakan {{STATIC_URL}} atau {% load staticfiles%} dengan {% static 'admin / js / core.js'%} ... diperlukan untuk Django 1.4+, karena seluruh namespace MEDIA_URL sudah tidak digunakan lagi dan dihapus. / admin / jsi18n teratasi dengan benar jika Anda memiliki ^ admin / dipetakan di server / urls.py
jeberle
barang bagus, berguna dalam kasus Django 1.4
Ashish
10

Dimulai pada Django 1.2 RC1, jika Anda menggunakan trik widge picker admin Django, yang berikut harus ditambahkan ke templat Anda, atau Anda akan melihat url ikon kalender yang direferensikan melalui "/ 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>
mshafrir
sumber
7

Melengkapi jawaban oleh Carl Meyer, saya ingin berkomentar bahwa Anda perlu meletakkan header itu di beberapa blok yang valid (di dalam header) di dalam template Anda.

{% 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 %}
Alex S.
sumber
Saya juga menemukan tautan ini bermanfaat: groups.google.ca/group/django-users/msg/1a40cf5f153cd23e
Alex. S.
6

Untuk Django> = 2.0

Catatan: Menggunakan widget admin untuk bidang waktu tanggal bukan ide yang baik karena style sheet admin dapat bertentangan dengan style sheet situs Anda jika Anda menggunakan bootstrap atau kerangka kerja CSS lainnya. Jika Anda membangun situs Anda di bootstrap gunakan widget bootstrap-datepicker saya django-bootstrap-datepicker-plus .

Langkah 1: Tambahkan javascript-catalogURL ke urls.pyfile proyek Anda (bukan aplikasi) .

from django.views.i18n import JavaScriptCatalog

urlpatterns = [
    path('jsi18n', JavaScriptCatalog.as_view(), name='javascript-catalog'),
]

Langkah 2: Tambahkan sumber daya JavaScript / CSS yang diperlukan ke template Anda.

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

Langkah 3: Gunakan widget admin untuk bidang input waktu-waktu di Anda 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,
        }
Munim Munna
sumber
5

Di bawah ini juga akan berfungsi sebagai jalan terakhir jika gagal di atas

class PaymentsForm(forms.ModelForm):
    class Meta:
        model = Payments

    def __init__(self, *args, **kwargs):
        super(PaymentsForm, self).__init__(*args, **kwargs)
        self.fields['date'].widget = SelectDateWidget()

Sama dengan

class PaymentsForm(forms.ModelForm):
    date = forms.DateField(widget=SelectDateWidget())

    class Meta:
        model = Payments

letakkan ini di forms.py Anda from django.forms.extras.widgets import SelectDateWidget

Dennis Kioko
sumber
1
itu memberi saya kesalahan 'DateField' objek tidak memiliki atribut 'needs_multipart_form'
madeeha ameer
3

Bagaimana dengan hanya menetapkan kelas untuk widget Anda dan kemudian mengikat kelas itu ke datepicker 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'

Dan beberapa JavaScript untuk templat:

  $(".datepicker").datepicker();
trubliphone
sumber
1

Solusi dan solusi yang diperbarui untuk SplitDateTime dengan wajib = Salah :

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'),
Sam A.
sumber
Saya juga telah mengisi tiket untuk memperbaiki kode widget SplitDateTime.djangoproject.com/ticket/12303
Sam A.
1

Saya menggunakan ini, sangat bagus, tetapi saya memiliki 2 masalah dengan templat:

  1. Saya melihat ikon kalender dua kali untuk setiap diajukan dalam template.
  2. Dan untuk TimeField saya memiliki ' Masukkan tanggal yang valid. '

    Ini adalah screenshot dari Formulir

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']
potemkin
sumber
0

Dalam Django 10. myproject / urls.py: di awal urlpatterns

  from django.views.i18n import JavaScriptCatalog

urlpatterns = [
    url(r'^jsi18n/$', JavaScriptCatalog.as_view(), name='javascript-catalog'),
.
.
.]

Di templat.html saya:

{% 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>
Jose Luis Quichimbo
sumber
0

Pengaturan Django Saya: 1.11 Bootstrap: 3.3.7

Karena tidak ada jawaban yang sepenuhnya jelas, saya membagikan kode templat saya yang sama sekali tidak menunjukkan kesalahan.

Setengah dari template:

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

Setengah bawah:

<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 %}
Arindam Roychowdhury
sumber
0

3 Juni 2020 (Semua jawaban tidak berhasil, Anda dapat mencoba solusi yang saya gunakan ini. Hanya untuk TimeField)

Gunakan sederhana Charfielduntuk bidang waktu ( mulai dan akhiri dalam contoh ini) dalam formulir.

forms.py

bisa kita gunakan Formatau di ModelFormsini.

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

Ubah input string menjadi objek waktu dalam tampilan.

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()
sandeep
sumber