Utilizzo dei widget data / ora di Django in forma personalizzata


171

Come posso utilizzare i widget di data e ora JavaScript che l'amministratore predefinito utilizza con la mia vista personalizzata?

Ho esaminato la documentazione dei moduli di Django e menziona brevemente django.contrib.admin.widgets, ma non so come usarlo?

Ecco il mio modello su cui voglio che venga applicato.

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

Inoltre, penso che dovrebbe essere notato che non ho davvero scritto un punto di vista per questo modulo, sto usando una vista generica. Ecco la voce da url.py:

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

E sono relativamente nuovo a tutto ciò che è Django / MVC / MTV, quindi per favore andate piano ...



Risposte:


159

La crescente complessità di questa risposta nel tempo e i numerosi hack richiesti, probabilmente dovrebbero metterti in guardia dal farlo. Si basa su dettagli dell'implementazione interna privi di documenti dell'amministratore, è probabile che si rompano di nuovo nelle versioni future di Django e non è più facile da implementare che trovare un altro widget del calendario JS e utilizzarlo.

Detto questo, ecco cosa devi fare se sei determinato a farlo funzionare:

  1. Definisci la tua sottoclasse ModelForm per il tuo modello (meglio inserirla in forms.py nella tua app) e digli di usare AdminDateWidget / AdminTimeWidget / AdminSplitDateTime (sostituisci 'mydate' ecc. Con i nomi dei campi appropriati del tuo modello):

    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. Cambia URLconf per passare 'form_class': ProductForm invece di 'model': Prodotto alla vista create_object generica (ciò significa "da my_app.forms importa ProductForm" anziché "da my_app.models import Product", ovviamente).

  3. Nell'intestazione del modello, includere {{form.media}} per generare i collegamenti ai file Javascript.

  4. E la parte caotica: i widget di data / ora dell'amministratore presumono che la roba di i18n JS sia stata caricata e richiedono anche core.js, ma non forniscono automaticamente nessuno dei due. Quindi nel modello sopra {{form.media}} avrai bisogno di:

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

    Puoi anche utilizzare il seguente CSS amministratore (grazie Alex per averlo menzionato):

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

Ciò implica che il supporto di amministrazione di Django (ADMIN_MEDIA_PREFIX) è in / media / admin / - puoi cambiarlo per la tua configurazione. Idealmente, useresti un processore di contesto per passare questi valori al tuo modello invece di codificarlo, ma questo va oltre lo scopo di questa domanda.

Ciò richiede anche che l'URL / my_admin / jsi18n / sia cablato manualmente alla vista django.views.i18n.javascript_catalog (o null_javascript_catalog se non si utilizza I18N). Devi farlo tu stesso invece di passare attraverso l'applicazione di amministrazione in modo che sia accessibile indipendentemente dal fatto che tu abbia effettuato l'accesso all'amministratore (grazie Jeremy per averlo segnalato). Codice di esempio per URLconf:

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

Infine, se si utilizza Django 1.2 o versioni successive, è necessario un codice aggiuntivo nel modello per consentire ai widget di trovare i propri file multimediali:

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

Grazie lupefiasco per questa aggiunta.


3
Il post più utile online per farlo, ma avendo finalmente visualizzato il widget data / ora e il campo popolato ora lo eliminerò perché nonostante abbia richiesto = Falso nel campo del modulo, ora mostra il messaggio "Inserisci una data / ora valida". per il mio campo non obbligatorio se lo lascio vuoto. Torna a jquery per me.
PhoebeB,

È questo il modo di andare su Django 1.1.1?
Nacho,

1
In Django 1.2 RC1 e versioni successive, è necessario apportare una piccola modifica a quanto sopra. Vedi: stackoverflow.com/questions/38601/...
mshafrir

1
Sai se questo vale ancora per Django 1.4? Anche se sono convinto di andare con Datepicker di jQuery, sono curioso di sapere se il team di Django avrebbe potuto rendere il loro widget più pubblicamente disponibile. (Sospetto di no, dato che questo QA sembra essere la maggior parte della documentazione in circolazione)
Giovanni C

1
Non c'è stato alcun movimento per rendere i widget di amministrazione più facilmente riutilizzabili al di fuori dell'amministratore, e non immagino che ce ne saranno. Sarebbe un sacco di lavoro e ci sono elementi con priorità molto più alta su cui lavorare. (Faccio parte del core team di Django, tra l'altro).
Carl Meyer,

64

Dal momento che la soluzione è hacker, penso che utilizzare il proprio widget data / ora con alcuni JavaScript sia più fattibile.


8
Sono d'accordo. Ho provato a farlo alcuni mesi fa con il mio progetto Django. Alla fine ho rinunciato e ho appena aggiunto il mio con jQueryUI. Ci sono voluti tutti i 10 minuti per farlo funzionare.
priestc,

8
Per la cronaca, sono d'accordo, ho votato a fondo questa risposta e non ho mai usato la tecnica nella mia risposta in un sito di produzione ;-)
Carl Meyer,

12

Sì, ho finito con l'override di / admin / jsi18n / url.

Ecco cosa ho aggiunto nel mio urls.py. Assicurati che sia sopra / admin / url

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

Ed ecco la funzione i18n_javascript che ho creato.

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

Oh, ottimo punto. Aggiungerò questo alla mia risposta sopra in modo che sia più facile per le persone trovare prima di avere problemi.
Carl Meyer,

12

Mi ritrovo a fare molto riferimento a questo post e ho scoperto che la documentazione definisce un modo leggermente meno complicato per sovrascrivere i widget predefiniti.

( Non è necessario sovrascrivere il metodo __init__ di ModelForm )

Tuttavia, è ancora necessario collegare correttamente JS e CSS come menziona 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

Tipi di campi di riferimento per trovare i campi modulo predefiniti.


5
Non sono d'accordo sul fatto che questo approccio sia meno aggressivo. Sembra più bello, certo, ma stai completamente ignorando i campi del modulo generati dal modulo del modello, che potrebbe anche cancellare le opzioni dai campi del modello, come help_text. La sostituzione di init modifica solo il widget e lascia il resto del campo modulo così com'era.
Carl Meyer,

11

Il mio codice principale per la versione 1.4 (alcuni nuovi e altri rimossi)

{% 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
eccezionale . lo stavo provando da 2 settimane e questo mi aiuta molto grazie
numerah

l'utilizzo di {{STATIC_URL}} o {% load staticfiles%} con {% static 'admin / js / core.js'%} ... è necessario per Django 1.4+, poiché l'intero spazio dei nomi MEDIA_URL è stato deprecato e rimosso. il / admin / jsi18n si risolve correttamente se hai ^ admin / mappato in server / urls.py
jeberle

bella roba, utile nel caso di Django 1.4
Ashish,

10

A partire da Django 1.2 RC1, se stai usando il trucco widge di selezione della data di amministrazione di Django, al modello devi aggiungere quanto segue, altrimenti vedrai l'URL dell'icona del calendario a cui si fa riferimento tramite "/ 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

Completando la risposta di Carl Meyer, vorrei commentare che è necessario inserire quell'intestazione in un blocco valido (all'interno dell'intestazione) all'interno del modello.

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

Per Django> = 2.0

Nota: l'utilizzo dei widget di amministrazione per i campi data-ora non è una buona idea poiché i fogli di stile dell'amministratore possono entrare in conflitto con i fogli di stile del sito nel caso in cui si stia utilizzando bootstrap o qualsiasi altro framework CSS. Se stai costruendo il tuo sito su bootstrap usa il mio widget bootstrap-datepicker django-bootstrap-datepicker-plus .

Passaggio 1: aggiungi l' javascript-catalogURL al file del tuo progetto (non dell'app) urls.py.

from django.views.i18n import JavaScriptCatalog

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

Passaggio 2: aggiungere le risorse JavaScript / CSS richieste al modello.

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

Passaggio 3: utilizzare i widget di amministrazione per i campi di input data-ora nel proprio 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

Quanto sotto funzionerà anche come ultima risorsa se quanto sopra fallito

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

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

Uguale a

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

    class Meta:
        model = Payments

mettilo nel tuo form.py from django.forms.extras.widgets import SelectDateWidget


1
mi dà l'errore 'DateField' l'oggetto non ha attributo 'needs_multipart_form'
madeeha ameer

3

Che ne dici di assegnare una classe al tuo widget e quindi associare quella classe al 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'

E alcuni JavaScript per il modello:

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


1

Soluzione aggiornata e soluzione alternativa per SplitDateTime con richiesto = Falso :

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

Ho anche compilato un ticket per correggere i widget SplitDateTime code.djangoproject.com/ticket/12303
Sam A.

1

Lo uso, è fantastico, ma ho 2 problemi con il modello:

  1. Vedo le icone del calendario due volte per ogni modello archiviato.
  2. E per TimeField ho ' Inserire una data valida. '

    Ecco uno screenshot del modulo

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

In Django 10. myproject / urls.py: all'inizio di urlpatterns

  from django.views.i18n import JavaScriptCatalog

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

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

My Django Setup: 1.11 Bootstrap: 3.3.7

Poiché nessuna delle risposte è completamente chiara, sto condividendo il mio codice modello che non presenta alcun errore.

Metà superiore del modello:

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

La metà inferiore:

<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 giugno 2020 (Tutte le risposte non hanno funzionato, puoi provare questa soluzione che ho usato. Solo per TimeField)

Utilizzare i Charfieldcampi semplici per l'ora ( inizio e fine in questo esempio) nei moduli.

forms.py

possiamo usare Formo ModelFormqui.

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

Converti l'input della stringa in oggetto tempo nelle viste.

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()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.