Django saat / tarih widget'larını özel biçimde kullanma


171

Varsayılan yöneticinin özel görünümümle kullandığı şık JavaScript tarih ve saat widget'larını nasıl kullanabilirim?

Django form belgelerine baktım ve kısaca django.contrib.admin.widgets'tan bahsediyor, ancak nasıl kullanılacağını bilmiyorum?

İşte benim şablonun uygulanmasını istiyorum.

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

Ayrıca, bu form için kendime bir görünüm yazmadığımı da belirtmeliyim, genel bir görüş kullanıyorum. İşte url.py'den giriş:

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

Ve ben tüm Django / MVC / MTV şey için ilgili yeni, bu yüzden lütfen kolay gidin ...



Yanıtlar:


159

Bu cevabın zaman içinde artan karmaşıklığı ve gerekli olan birçok hack, muhtemelen bunu hiç yapmamanız konusunda sizi uyarmalıdır. Yöneticinin belgelenmemiş dahili uygulama ayrıntılarına dayanıyor, Django'nun gelecekteki sürümlerinde tekrar kırılması muhtemel ve sadece başka bir JS takvim widget'ı bulmak ve bunu kullanmaktan daha kolay değil.

Bununla birlikte, bu işi yapmaya kararlıysanız yapmanız gerekenler:

  1. Modeliniz için kendi ModelForm alt sınıfınızı tanımlayın (uygulamanızda forms.py dosyasına koymak en iyisidir) ve YöneticiDateWidget / AdminTimeWidget / AdminSplitDateTime'ı ('mydate' vb. Modelinizden uygun alan adlarıyla değiştirmesini söyleyin):

    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'unuzu 'model' yerine 'form_class': ProductForm değerini genel jenerasyon_object görünümüne geçirecek şekilde değiştirin (elbette "my_app.models import Product" yerine "my_app.forms import ProductForm" anlamına gelir).

  3. Şablonunuzun başına, Javascript dosyalarına bağlantıların çıktısını almak için {{form.media}} ekleyin.

  4. Ve hacky kısmı: yönetici tarih / saat widget'ları, i18n JS öğelerinin yüklendiğini varsayar ve ayrıca core.js gerektirir, ancak ikisini de otomatik olarak sağlamaz. Dolayısıyla, {{form.media}} üzerindeki şablonunuzda şunlara ihtiyacınız olacak:

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

    Aşağıdaki yönetici CSS'sini de kullanmak isteyebilirsiniz (bundan bahsettiğiniz için Alex'e teşekkür ederiz ):

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

Bu, Django'nun yönetici ortamının (ADMIN_MEDIA_PREFIX) / media / admin / konumunda olduğu anlamına gelir - kurulumunuz için bunu değiştirebilirsiniz. İdeal olarak, bu değerleri şablonunuza sabit kodlamak yerine aktarmak için bir bağlam işlemcisi kullanırsınız, ancak bu sorunun kapsamı dışındadır.

Bu aynı zamanda URL / my_admin / jsi18n / komutunun django.views.i18n.javascript_catalog görünümüne (veya I18N kullanmıyorsanız null_javascript_catalog) manuel olarak bağlanmasını gerektirir. Yönetici uygulamasına girmek yerine bunu kendiniz yapmanız gerekir, böylece yöneticiye giriş yapıp yapmadığınıza bakılmaksızın erişilebilir (bunu işaret ettiğiniz için Jeremy'ye teşekkür ederiz ). URL bağlantınız için örnek kod:

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

Son olarak, Django 1.2 veya üstünü kullanıyorsanız, widget'ların medyalarını bulmasına yardımcı olmak için şablonunuzda bazı ek kodlara ihtiyacınız vardır:

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

Bu ekleme için lupefiasco teşekkürler .


3
Bunu yapmak için çevrimiçi en yararlı yazı ancak nihayet tarih / saat widget'ı var ve alan doldurmak şimdi bunu çıkaracağım çünkü zorunlu = Form alanına yanlış olmasına rağmen şimdi "Geçerli bir tarih / saat girin" mesajını gösteriyor. boş bırakmazsam zorunlu olmayan alanım için. Benim için jquery'ye geri dön.
PhoebeB

Django 1.1.1'e devam etmenin yolu bu mu?
Nacho

1
Django 1.2 RC1 ve sonrasında, yukarıdakilerde küçük bir değişiklik yapmanız gerekir. Bakınız: stackoverflow.com/questions/38601/…
mshafrir

1
Bunun hala Django 1.4 için geçerli olup olmadığını biliyor musunuz? Her ne kadar jQuery'nin tarih seçicisine gitmeye ikna olmama rağmen, Django ekibinin widget'larını herkese açık hale getirip getiremeyeceğini merak ediyorum. (Bu QA en çok dokümantasyon gibi göründüğünden şüphelenmiyorum)
John C

1
Yönetici pencere öğelerini yönetici dışında daha kolay bir şekilde yeniden kullanılabilir hale getirme yönünde bir hareket olmamıştır ve herhangi bir şey olacağını tahmin etmem. Çok fazla iş olurdu ve üzerinde çalışılacak daha yüksek öncelikli öğeler var. (Ben Django çekirdek ekibinin bir parçasıyım, btw).
Carl Meyer

64

Çözüm hackish olduğu için, bazı JavaScript ile kendi tarih / saat widget'ınızı kullanmak daha uygun olduğunu düşünüyorum.


8
Katılıyorum. Bunu birkaç ay önce django projemle yapmaya çalıştım. Sonunda vazgeçtim ve sadece jQueryUI ile kendi ekledi. Çalışması için 10 dakika sürdü.
Priestc

8
Kayıt için katılıyorum, bu cevabı iptal ettim ve aslında bir üretim tesisindeki cevabımda tekniği hiç kullanmadım ;-)
Carl Meyer

12

Evet, / admin / jsi18n / url'yi geçersiz kıldım.

İşte urls.py dosyama eklediklerim. / Admin / url değerinin üzerinde olduğundan emin olun

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

Ve burada oluşturduğum i18n_javascript işlevi.

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

Oh, çok iyi bir nokta. Bunu yukarıdaki cevabıma ekleyeceğim, böylece insanların sorun yaşamadan bulmaları daha kolay.
Carl Meyer

12

Kendimi bu gönderiye çok atıfta bulunuyorum ve belgelerin varsayılan widget'ları geçersiz kılmak için biraz daha kötü bir yol tanımladığını gördüm .

( ModelForm'un __init__ yöntemini geçersiz kılmanıza gerek yoktur )

Bununla birlikte, JS ve CSS'nizi Carl'ın belirttiği gibi uygun şekilde kablolamanız gerekir.

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

Varsayılan form alanlarını bulmak için Başvuru Alanı Türleri .


5
Bu yaklaşımın daha az hileli olduğunu kabul etmiyorum. Daha hoş görünüyor, ama model formunun oluşturduğu form alanlarını tamamen geçersiz kılıyorsunuz, bu da help_text gibi model alanlarındaki seçenekleri silebilir. Geçersiz kılma init yalnızca widget'ı değiştirir ve form alanının geri kalanını olduğu gibi bırakır.
Carl Meyer

11

1.4 sürümü için kafa kodum (bazıları yeni ve bazıları kaldırıldı)

{% 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
harika. Bu son 2 hafta
deniyordu

Django 1.4+ için {{STATIC_URL}} veya {% load staticfiles%} ile {% static 'admin / js / core.js'%} ... kullanılması, MEDIA_URL ad alanının tamamı kullanımdan kaldırıldığı ve kaldırıldığı için Django 1.4+ için gereklidir. / admin / jsi18n, sunucu / urls.py
dosyasında

güzel şeyler, Django 1.4 durumunda yararlı
Ashish

10

Django 1.2 RC1'den başlayarak, Django yönetici tarih seçici widge hilesi kullanıyorsanız, şablonunuza aşağıdakilerin eklenmesi gerekir veya takvim simgesi url'sinin "/ missing-admin-media-prefix aracılığıyla başvurulduğunu görürsünüz. /".

{% 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'ın cevabını tamamlayarak, söz konusu üstbilgiyi şablonunuzdaki geçerli bir bloğa (üstbilgi içinde) koymanız gerektiğini yorumlamak istiyorum.

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


6

Django için> = 2.0

Not: Tarih / saat alanları için yönetici widget'ları kullanmak iyi bir fikir değildir, çünkü bootstrap veya başka bir CSS çerçevesi kullanıyorsanız yönetici stil sayfaları site stil sayfalarınızla çakışabilir. Sitenizi bootstrap üzerinde oluşturuyorsanız, bootstrap-datepicker widget'ımı django-bootstrap-datepicker-plus kullanın .

1. Adım:javascript-catalog Projenizin (uygulamanın değil) urls.pydosyasına URL ekleyin .

from django.views.i18n import JavaScriptCatalog

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

2. Adım: Şablonunuza gerekli JavaScript / CSS kaynaklarını ekleyin.

<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. Adım: Bilgisayarınızdaki tarih ve saat giriş alanları için yönetici widget'larını kullanın 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

Aşağıdakiler de başarısız olursa, son çare olarak çalışacaktır.

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

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

İle aynı

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

    class Meta:
        model = Payments

bunu forms.py'nize koyun from django.forms.extras.widgets import SelectDateWidget


1
'DateField' nesnesinin 'needs_multipart_form' özelliği yok
madeeha ameer

3

Widget'ınıza sadece bir sınıf atamaya ve ardından bu sınıfı JQuery tarih seçicisine bağlamaya ne dersiniz?

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'

Ve şablon için bazı JavaScriptler:

  $(".datepicker").datepicker();


1

SplitDateTime için gerekli = False ile güncellenmiş çözüm ve geçici çözüm :

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

Ayrıca SplitDateTime widget'ları kodunu
Sam A.

1

Bunu kullanıyorum, harika, ama şablonla ilgili 2 problemim var:

  1. Şablondaki her dosya için takvim simgelerini iki kez görüyorum .
  2. TimeField için ' Geçerli bir tarih girin. '

    Formun ekran görüntüsü

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.'da projem / urls.py: urlpatterns başında

  from django.views.i18n import JavaScriptCatalog

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

Template.html dosyamda:

{% 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 Kurulumum: 1.11 Bootstrap: 3.3.7

Cevapların hiçbiri tamamen açık olmadığından, hiçbir hata göstermeyen şablon kodumu paylaşıyorum.

Şablonun ilk yarısı:

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

Alt yarısı:

<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 Haziran 2020 (Tüm yanıtlar işe yaramadı, kullandığım bu çözümü deneyebilirsiniz. Sadece TimeField için)

Formlarda Charfieldzaman alanları için ( bu örnekte başlangıç ve bitiş ) basit kullanın .

forms.py

kullanabileceğimiz Formveya ModelFormburada.

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

Görünümlerde dize girişini zaman nesnesine dönüştürün.

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()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.