Verwenden von Django-Widgets für Uhrzeit und Datum in benutzerdefinierter Form

171

Wie kann ich die raffinierten JavaScript-Widgets für Datum und Uhrzeit verwenden, die der Standardadministrator für meine benutzerdefinierte Ansicht verwendet?

Ich habe die Dokumentation zu Django-Formularen durchgesehen und sie erwähnt kurz django.contrib.admin.widgets, aber ich weiß nicht, wie ich sie verwenden soll.

Hier ist meine Vorlage, auf die ich sie anwenden möchte.

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

Ich denke auch, dass ich selbst keine Ansicht für dieses Formular geschrieben habe. Ich verwende eine generische Ansicht. Hier ist der Eintrag aus der url.py:

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

Und ich bin relevant neu in der ganzen Django / MVC / MTV-Sache, also bitte gehen Sie einfach ...

Josh Hunt
quelle

Antworten:

159

Die zunehmende Komplexität dieser Antwort im Laufe der Zeit und die vielen erforderlichen Hacks sollten Sie wahrscheinlich davor warnen, dies überhaupt zu tun. Es basiert auf undokumentierten internen Implementierungsdetails des Administrators, wird in zukünftigen Versionen von Django wahrscheinlich wieder kaputt gehen und ist nicht einfacher zu implementieren, als nur ein anderes JS-Kalender-Widget zu finden und dieses zu verwenden.

Das heißt, hier ist, was Sie tun müssen, wenn Sie entschlossen sind, diese Arbeit zu machen:

  1. Definieren Sie Ihre eigene ModelForm-Unterklasse für Ihr Modell (am besten in forms.py in Ihrer App) und weisen Sie es an, AdminDateWidget / AdminTimeWidget / AdminSplitDateTime zu verwenden (ersetzen Sie 'mydate' usw. durch die richtigen Feldnamen aus Ihrem Modell):

    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. Ändern Sie Ihre URLconf so, dass 'form_class': ProductForm anstelle von 'model': Product an die generische Ansicht create_object übergeben wird (dies bedeutet natürlich "from my_app.forms import ProductForm" anstelle von "from my_app.models import Product").

  3. Fügen Sie im Kopf Ihrer Vorlage {{form.media}} ein, um die Links zu den Javascript-Dateien auszugeben.

  4. Und der hackige Teil: Die Widgets für Datum und Uhrzeit des Administrators setzen voraus, dass das i18n JS-Zeug geladen wurde, und erfordern auch core.js, stellen jedoch keines automatisch bereit. In Ihrer Vorlage über {{form.media}} benötigen Sie also:

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

    Möglicherweise möchten Sie auch das folgende Admin-CSS verwenden (danke Alex, dass Sie dies erwähnt haben):

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

Dies bedeutet, dass sich Djangos Administratormedien (ADMIN_MEDIA_PREFIX) unter / media / admin / befinden. Sie können dies für Ihr Setup ändern. Idealerweise verwenden Sie einen Kontextprozessor, um diese Werte an Ihre Vorlage zu übergeben, anstatt sie fest zu codieren. Dies würde jedoch den Rahmen dieser Frage sprengen.

Dies erfordert auch, dass die URL / my_admin / jsi18n / manuell mit der Ansicht django.views.i18n.javascript_catalog (oder null_javascript_catalog, wenn Sie I18N nicht verwenden) verbunden ist. Sie müssen dies selbst tun, anstatt die Administratoranwendung zu durchlaufen, damit Sie darauf zugreifen können, unabhängig davon, ob Sie beim Administrator angemeldet sind (danke Jeremy, dass Sie darauf hingewiesen haben). Beispielcode für Ihre URLconf:

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

Wenn Sie Django 1.2 oder höher verwenden, benötigen Sie zusätzlichen Code in Ihrer Vorlage, damit die Widgets ihre Medien finden können:

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

Danke lupefiasco für diesen Zusatz.

Carl Meyer
quelle
3
Der hilfreichste Online-Beitrag dazu, aber nachdem endlich das Datums- / Uhrzeit-Widget angezeigt wurde und das Feld ausgefüllt wurde, wird es jetzt entfernt, da trotz der Anforderung = Falsch im Formularfeld jetzt die Meldung "Geben Sie ein gültiges Datum / eine gültige Uhrzeit ein" angezeigt wird. für mein nicht benötigtes Feld, wenn ich es leer lasse. Zurück zu jquery für mich.
PhoebeB
Ist dies der richtige Weg für Django 1.1.1?
Nacho
1
In Django 1.2 RC1 und höher müssen Sie eine kleine Änderung an den oben genannten Punkten vornehmen. Siehe: stackoverflow.com/questions/38601/…
mshafrir
1
Wissen Sie, ob dies noch für Django 1.4 gilt? Obwohl ich überzeugt bin, mit jQuerys Datepicker zu arbeiten, bin ich gespannt, ob das Django-Team sein Widget möglicherweise öffentlicher verfügbar gemacht hat. (Ich vermute nicht, da diese Qualitätssicherung die meiste Dokumentation zu sein scheint)
John C
1
Es hat keine Bewegung gegeben, die Admin-Widgets außerhalb des Administrators leichter wiederverwendbar zu machen, und ich würde nicht vermuten, dass es welche geben wird. Es wäre viel Arbeit, und es gibt Dinge mit viel höherer Priorität, an denen gearbeitet werden muss. (Ich bin übrigens Teil des Django-Kernteams).
Carl Meyer
64

Da die Lösung hackisch ist, denke ich, dass die Verwendung Ihres eigenen Datums- / Zeit-Widgets mit etwas JavaScript praktikabler ist.

zgoda
quelle
8
Genau. Ich habe es vor einigen Monaten mit meinem Django-Projekt versucht. Ich gab schließlich auf und fügte nur meine eigene mit jQueryUI hinzu. Es dauerte alle 10 Minuten, bis es funktionierte.
Priester
8
Ich stimme zu, ich habe diese Antwort positiv bewertet und die Technik in meiner Antwort in einer Produktionsstätte nie verwendet ;-)
Carl Meyer
12

Ja, am Ende habe ich die / admin / jsi18n / url überschrieben.

Folgendes habe ich in meiner urls.py hinzugefügt. Stellen Sie sicher, dass es über der / admin / url liegt

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

Und hier ist die von mir erstellte Funktion i18n_javascript.

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

quelle
Oh, sehr guter Punkt. Ich werde dies zu meiner obigen Antwort hinzufügen, damit die Leute sie leichter finden können, bevor sie Probleme haben.
Carl Meyer
12

Ich verweise häufig auf diesen Beitrag und stelle fest, dass die Dokumentation eine etwas weniger hackige Methode zum Überschreiben von Standard-Widgets definiert.

( Die __init__- Methode von ModelForm muss nicht überschrieben werden. )

Sie müssen jedoch weiterhin JS und CSS entsprechend verkabeln, wie Carl erwähnt.

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

Referenzfeldtypen die Standardformularfelder zu finden.

Mönch
quelle
5
Ich bin nicht der Meinung, dass dieser Ansatz weniger hackisch ist. Es sieht zwar besser aus, aber Sie überschreiben die vom Modellformular generierten Formularfelder vollständig, wodurch auch Optionen aus den Modellfeldern wie help_text gelöscht werden können. Durch das Überschreiben von init wird nur das Widget geändert und der Rest des Formularfelds bleibt unverändert .
Carl Meyer
11

Mein Kopfcode für die Version 1.4 (einige neu und einige entfernt)

{% 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
quelle
3
genial . Ich habe dies in den letzten 2 Wochen versucht und dies hilft mir vielen Dank
Numerah
Die Verwendung von {{STATIC_URL}} oder {% load staticfiles%} mit {% static 'admin / js / core.js'%} ... ist für Django 1.4+ erforderlich, da der gesamte MEDIA_URL-Namespace veraltet und entfernt wurde. Das / admin / jsi18n wird ordnungsgemäß aufgelöst, wenn Sie ^ admin / in server / urls.py
jeberle
nette Sachen, nützlich im Fall von Django 1.4
Ashish
10

Wenn Sie ab Django 1.2 RC1 den Widge-Trick für die Datumsauswahl von Django-Administratoren verwenden, muss Folgendes zu Ihrer Vorlage hinzugefügt werden. Andernfalls wird die URL des Kalendersymbols durch das Präfix "/ missing-admin-media-prefix" angezeigt / ".

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

Ergänzend zur Antwort von Carl Meyer möchte ich darauf hinweisen, dass Sie diesen Header in einen gültigen Block (innerhalb des Headers) in Ihrer Vorlage einfügen müssen.

{% 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.
quelle
Ich fand diesen Link auch nützlich: groups.google.ca/group/django-users/msg/1a40cf5f153cd23e
Alex. S.
6

Für Django> = 2,0

Hinweis: Die Verwendung von Admin-Widgets für Datums- und Uhrzeitfelder ist keine gute Idee, da Admin-Stylesheets mit Ihren Site-Stylesheets in Konflikt stehen können, wenn Sie Bootstrap oder andere CSS-Frameworks verwenden. Wenn Sie Ihre Site auf Bootstrap erstellen, verwenden Sie mein Bootstrap-Datepicker-Widget django-bootstrap-datepicker-plus .

Schritt 1: Fügen javascript-catalogSie der urls.pyDatei Ihres Projekts (nicht der App) eine URL hinzu .

from django.views.i18n import JavaScriptCatalog

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

Schritt 2: Fügen Sie Ihrer Vorlage die erforderlichen JavaScript / CSS-Ressourcen hinzu.

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

Schritt 3: Verwenden Sie Admin-Widgets für Datums- und Uhrzeit-Eingabefelder in Ihrem 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
quelle
5

Das Folgende funktioniert auch als letzter Ausweg, wenn das Obige fehlgeschlagen ist

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

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

Gleich wie

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

    class Meta:
        model = Payments

Tragen Sie dies in Ihre forms.py ein from django.forms.extras.widgets import SelectDateWidget

Dennis Kioko
quelle
1
es gibt mir Fehler 'DateField' Objekt hat kein Attribut 'need_multipart_form'
madeeha ameer
3

Wie wäre es, wenn Sie Ihrem Widget nur eine Klasse zuweisen und diese Klasse dann an den JQuery-Datepicker binden?

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'

Und etwas JavaScript für die Vorlage:

  $(".datepicker").datepicker();
Trubliphone
quelle
1

Aktualisierte Lösung und Problemumgehung für SplitDateTime mit erforderlich = Falsch :

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.
quelle
Ich habe auch ein Ticket gefüllt, um die SplitDateTime-Widgets zu reparieren. Code.djangoproject.com/ticket/12303
Sam A.
1

Ich benutze dies, es ist großartig, aber ich habe 2 Probleme mit der Vorlage:

  1. Ich sehe die Kalendersymbole zweimal für jede in der Vorlage abgelegte Vorlage.
  2. Und für TimeField habe ich ' Geben Sie ein gültiges Datum ein. '

    Hier ist ein Screenshot des Formulars

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

In Django 10. myproject / urls.py: am Anfang von URL-Mustern

  from django.views.i18n import JavaScriptCatalog

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

In meiner 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>
Jose Luis Quichimbo
quelle
0

Mein Django-Setup: 1.11 Bootstrap: 3.3.7

Da keine der Antworten vollständig klar ist, teile ich meinen Vorlagencode, der überhaupt keine Fehler enthält.

Obere Hälfte der Vorlage:

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

Untere Hälfte:

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

3. Juni 2020 (Alle Antworten haben nicht funktioniert. Sie können diese von mir verwendete Lösung ausprobieren. Nur für TimeField.)

Verwenden Sie in Formularen einfach Charfieldfür Zeitfelder ( in diesem Beispiel Start und Ende ).

forms.py

wir können Formoder ModelFormhier verwenden.

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

Konvertieren Sie die Eingabe von Zeichenfolgen in Ansichten in ein Zeitobjekt.

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
quelle