it-swarm.com.de

Django: Wie erstelle ich ein benutzerdefiniertes Formular-Widget?

Ich finde es schwierig, eine Dokumentation zu finden, wie man ein benutzerdefiniertes Widget schreibt.

Meine Fragen sind:

  • Kann ich ein benutzerdefiniertes Widget erstellen, wenn es gleichermaßen für die Administrationsoberfläche oder für normale Formulare verwendet wird?
  • Welches Widget sollte ich einer Unterklasse zuordnen, wenn ich dem Benutzer erlauben möchte, eine Liste von Elementen zu bearbeiten? Welche Methoden des Widget muss ich überschreiben/implementieren?
  • Welche Widget-Methode ist dafür verantwortlich, von der Eingabe des Benutzers zum Datenmodell zurückzukehren?

Vielen Dank.

51
Nick Heiner

Sie haben Recht damit, dass Django keine Dokumentation zu diesem bestimmten Thema bereitstellt. Ich rate Ihnen, sich die eingebauten Widgets in Django.forms.widgets anzuschauen (ich verweise auf Klassen aus diesem Modul weiter unten).

Wenn ich ein benutzerdefiniertes Widget erstelle, kann es gleichermaßen für die Administrationsoberfläche oder für normale Formulare verwendet werden?

Der Administrator überschreibt einige Widgets (siehe Django.contrib.admin.options.FORMFIELD_FOR_DBFIELD_DEFAULTS ). Sie können wahrscheinlich ModelAdmin in Unterklassen unterteilen und das Attribut _formfield_overrides_ ändern, aber ich habe noch nie etwas mit ModelAdmin getan, daher kann ich hier nicht helfen ...

Wenn ich dem Benutzer erlauben möchte, eine Liste von Elementen zu bearbeiten, welches Widget soll ich unterteilen? Welche Methoden des Widgets muss ich überschreiben/implementieren?

Ihr Widget hat wahrscheinlich nichts mit den Standard-Widgets gemeinsam (mit Select, falls vorhanden ?!). Unterklasse von Widget und wenn Sie ein gemeinsames Muster mit eingebauten finden, können Sie es später noch ändern.

Implementieren Sie die folgenden Methoden:

  • render(self, name, value, attrs=None, renderer=None)

    Schauen Sie sich _Input.render_ für ein einfaches Beispiel an. Es werden auch benutzerdefinierte Attribute unterstützt, die im HTML enthalten sind. Möglicherweise möchten Sie auch "id" -Attribute hinzufügen (siehe _MultipleHiddenInput.render_). Vergessen Sie nicht, _mark_safe_ zu verwenden, wenn Sie HTML direkt ausgeben. Wenn Sie ein ziemlich komplexes Widget haben, können Sie das Vorlagen-Rendering verwenden ( Beispiel ).

  • _has_changed(self, initial, data)

    Wahlweise. Wird in admin verwendet, um Meldungen zu Änderungen zu protokollieren.

Welche Widget-Methode ist dafür verantwortlich, von der Benutzereingabe zurück zum Datenmodell zu gelangen?

Das hat nichts mit dem Widget zu tun - Django kann nicht wissen, welches Widget in einer früheren Anfrage verwendet wurde. Es können nur die vom Formular gesendeten Formulardaten (POST) verwendet werden. Daher wird die Feldmethode _Field.to_python_ verwendet, um Eingaben in den Datentyp Python zu konvertieren (kann ValidationError auslösen, wenn die Eingabe ungültig ist).

47
AndiDog

Django <1,11

Zusätzlich zu den anderen Antworten ist dies ein kleines Codebeispiel eines benutzerdefinierten Widgets:

widgets.py:

from Django.forms.widgets import Widget
from Django.template import loader
from Django.utils.safestring import mark_safe


class MyWidget(Widget):
    template_name = 'myapp/my_widget.html'

    def get_context(self, name, value, attrs=None):
        return {'widget': {
            'name': name,
            'value': value,
        }}

    def render(self, name, value, attrs=None):
        context = self.get_context(name, value, attrs)
        template = loader.get_template(self.template_name).render(context)
        return mark_safe(template)

my_widget.html:

<textarea id="mywidget-{{ widget.name }}" name="{{ widget.name }}">
{% if widget.value %}{{ widget.value }}{% endif %}</textarea>

Django 1.11

Widgets werden jetzt mit der Form-Rendering-API gerendert.

21
Wtower

HINWEIS: Hier gibt es drei Fragen. Die ersten beiden Fragen finden Sie in der ausführlicheren Antwort von AndiDog. Ich beantworte hier nur die dritte Frage:

F. Welche Widget-Methode ist dafür verantwortlich, von der Eingabe des Benutzers zum Datenmodell zurückzukehren?

A. Die value_from_datadict-Methode ist eine Art Umkehrung der render-Methode eines Widgets. Diese Methode ist vermutlich das, worauf sich die Django-Dokumente in Widgets beziehen, wenn sie sagt: "Das Widget übernimmt das Rendern des HTML-Codes und die Extraktion von Daten aus einem GET/POST-Wörterbuch, das dem Widget entspricht." Zu diesem Punkt gibt es nichts weiter in den Dokumenten, aber Sie können sehen, wie es im Code für die integrierten Widgets funktioniert.

5
Ghopper21

Normalerweise erbe ich von einem der vorhandenen Widgets, füge eine neue gewünschte Eigenschaft hinzu und ändere dann eine Render-Methode. Hier ist ein Beispiel für ein filterbares Auswahl-Widget, das ich implementiert habe. Die Filterung erfolgt über jquery mobile.

class FilterableSelectWidget(forms.Select):
    def __init__(self, attrs=None, choices=()):
        super(FilterableSelectWidget, self).__init__(attrs, choices)
        # choices can be any iterable, but we may need to render this widget
        # multiple times. Thus, collapse it into a list so it can be consumed
        # more than once.
        self._data_filter = {}

    @property
    def data_filter(self):
        return self._data_filter

    @data_filter.setter
    def data_filter(self, attr_dict):
        self._data_filter.update(attr_dict)

    def render_option(self, selected_choices, option_value, option_label):
        option_value = force_text(option_value)
        if option_value in selected_choices:
            selected_html = mark_safe(' selected="selected"')
            if not self.allow_multiple_selected:
                # Only allow for a single selection.
                selected_choices.remove(option_value)
        else:
            selected_html = ''
        # use self.data_filter
        filtertext = self.data_filter.get(option_value)
        data_filtertext = 'data-filtertext="{filtertext}"'.\
            format(filtertext=filtertext) if filtertext else ''
        return format_html('<option value="{0}"{1} {3}>{2}</option>',
                           option_value,
                           selected_html,
                           force_text(option_label),
                           mark_safe(data_filtertext))

Dann setze ich in den Ansichten, in denen ich ein Formular erstelle, den data_filter für das Feld.

        some_form.fields["some_field"] = \
            forms.ChoiceField(choices=choices,
                              widget=FilterableSelectWidget)
        some_form.fields["some_field"].widget.data_filter = \
            data_filter
3
Al Conrad

Die Dokumentation auf Djangos Seite hilft dabei überhaupt nicht. Vorschläge zur Anpassung von Widgets, hier , brechen die Verwendung von form.as_p() ab, wodurch der Wert von Formularen, wie in Django dargestellt, gefährdet wird, d. H. Eine Zusammenstellung von Widgets.

Die Lösungen, die mir am besten gefallen haben, sind Floppyforms . Es erleichtert die Definition von Widgets mithilfe von Vorlagen und ist ein (fast) transparenter Ersatz für Djangos eigenes Formularmodul. Es verfügt über eine hervorragende Dokumentation und ist leicht zu erlernen.

0
Peter Shannon