Definieren Sie die CSS-Klasse in Django-Formularen

192

Angenommen, ich habe ein Formular

class SampleClass(forms.Form):
    name = forms.CharField(max_length=30)
    age = forms.IntegerField()
    django_hacker = forms.BooleanField(required=False)

Gibt es eine Möglichkeit für mich, CSS-Klassen für jedes Feld so zu definieren, dass ich jQuery basierend auf der Klasse auf meiner gerenderten Seite verwenden kann?

Ich hatte gehofft, das Formular nicht manuell erstellen zu müssen.

Ashchristopher
quelle
9
wow, alle stimmen darüber ab? docs.djangoproject.com/de/dev/ref/forms/widgets/…
Skylar Saveland
10
@skyl Ich würde es als Hinweis darauf nehmen, dass es nicht leicht in den Django-Dokumenten zu finden ist. Ich habe auch mehrere Google-Suchanfragen durchgeführt und konnte diese nicht finden. Daher freue ich mich über die Frage und sie wird positiv bewertet.
Nils
Ich weiß, dass es ein alter Thread ist, aber in Django-Formularfeldern haben sie jetzt eine id_fieldname-Klasse.
Ratsimihah
1
In dieser Antwort
erfahren Sie,

Antworten:

182

Eine weitere Lösung, die keine Änderungen im Python-Code erfordert und daher besser für Designer und einmalige Präsentationsänderungen geeignet ist : Django-Widget-Tweaks . Hoffe, jemand wird es nützlich finden.

Mikhail Korobov
quelle
61
Die einzig vernünftige Lösung muss ich sagen. Danke dir!. Python-Code, und insbesondere in der Definition des Formulars, ist der letzte Ort, an dem Dinge für das Styling eingefügt werden - diese gehören definitiv zu den Vorlagen.
Boris Chervenkov
5
Dies ist eine großartige Bibliothek! Es ist eine Schande, dass diese Antwort unten vergraben ist.
Chris Lacasse
1
Großartig für Designer! :-)
hobbes3
1
Ausgezeichnet! Ich hatte einige Filter entwickelt, um diese Dinge zu tun, aber dieses Projekt ist viel leistungsfähiger. Vielen Dank!
Msbrogli
1
Eigentlich funktioniert es auch für Jinja2 :-) Ich habe die Reihenfolge des sicheren Fileters geändert und Klammern anstelle des Doppelpunkts hinzugefügt {{myform.email | add_class ("css_class_1 css_class_2") | safe}}. Danke, dass Sie dies geschrieben haben. es sollte ein Teil von Django sein.
David Dehghan
92

Beantwortete meine eigene Frage. Seufzer

http://docs.djangoproject.com/de/dev/ref/forms/widgets/#django.forms.Widget.attrs

Ich wusste nicht, dass es an den Widget-Konstruktor übergeben wurde.

Ashchristopher
quelle
2
Bedeutet das, dass es nicht mit der ModelForm-Klasse verwendet werden kann?
Xster
2
Nein, Sie müssen lediglich das Formularfeld im Modellformular explizit definieren, damit Sie das Widget definieren können. Oder verwenden Sie die unten aufgeführte Lösung, um zu vermeiden, dass Sie mit dem Formularfeld herumspielen müssen.
Tom
78

Hier ist eine weitere Lösung zum Hinzufügen von Klassendefinitionen zu den Widgets, nachdem die Felder in der Klasse deklariert wurden.

def __init__(self, *args, **kwargs):
    super(SampleClass, self).__init__(*args, **kwargs)
    self.fields['name'].widget.attrs['class'] = 'my_class'
Ashchristopher
quelle
4
Bei ModelForms ist dies häufig besser, da Sie das verwendete Standardformularfeld nicht kennen müssen und verschiedene Klassen basierend auf den Laufzeitbedingungen dynamisch festlegen können. Sauberer als Meta-Coding-Hacks ...
Daniel Naab
1
Dies setzt voraus, dass Sie für jedes Feld in einem Formular eine Eingabe wünschen, was häufig nicht der Fall ist. Ein Formular ist nicht unbedingt an ein Modell gebunden - es kann an viele Modelle gebunden sein. Für den Fall, dass die Modellfelder und die Formularfelder eine 1: 1-Beziehung haben, ist ModelForm eine viel bessere Wahl.
Ashchristopher
44

Verwenden Sie Django-Widget-Tweaks , es ist einfach zu bedienen und funktioniert ziemlich gut.

Andernfalls kann dies mithilfe eines benutzerdefinierten Vorlagenfilters erfolgen.

In Anbetracht dessen, dass Sie Ihr Formular folgendermaßen rendern:

<form action="/contact/" method="post">
    {{ form.non_field_errors }}
    <div class="fieldWrapper">
        {{ form.subject.errors }}
        <label for="id_subject">Email subject:</label>
        {{ form.subject }}
    </div>
</form>

form.subject ist eine Instanz von BoundField mit der Methode as_widget .

Sie können einen benutzerdefinierten Filter "addcss" in "my_app / templatetags / myfilters.py" erstellen.

from django import template

register = template.Library()

@register.filter(name='addcss')
def addcss(value, arg):
    css_classes = value.field.widget.attrs.get('class', '').split(' ')
    if css_classes and arg not in css_classes:
        css_classes = '%s %s' % (css_classes, arg)
    return value.as_widget(attrs={'class': css_classes})

Und dann wenden Sie Ihren Filter an:

{% load myfilters %}
<form action="/contact/" method="post">
    {{ form.non_field_errors }}
    <div class="fieldWrapper">
        {{ form.subject.errors }}
        <label for="id_subject">Email subject:</label>
        {{ form.subject|addcss:'MyClass' }}
    </div>
</form>

form.subjects werden dann mit der CSS-Klasse "MyClass" gerendert.

Ich hoffe das hilft.

BEARBEITEN 1

  • Aktualisieren Sie den Filter gemäß der Antwort von dimyG

  • Django-Widget-Tweak-Link hinzufügen

BEARBEITEN 2

  • Aktualisiere den Filter gemäß Bhyds Kommentar
Charlesthk
quelle
3
Das ist toll! Es ist TROCKEN und trennt die Anzeigeebene von der Kontrollebene. +1 von mir!
Alewwisnia
1
Jetzt habe ich jedoch einen Nachteil bemerkt. Wenn ich eine Klasse in Widget-Attributen definiere, werden sie von diesem 'addcss'-Filter überschrieben. Haben Sie Ideen, wie Sie das zusammenführen können?
Alewwisnia
1
Ich meine, as_widget () überschreibt attrs. Wie kann sichergestellt werden, dass vorhandene Attribute verwendet und um neue erweitert werden?
Alewwisnia
Siehe diese Antwort, wie man bestehende
Feldklassen
get('class', None).split(' ')schlägt fehl, wenn das Tag nicht über das zurückgegebene Klassenattribut verfügt None. Ändern es zu get('class', '').split(' ')funktioniert
dtasev
32

Erweiterung der Methode, auf die unter docs.djangoproject.com verwiesen wird:

class MyForm(forms.Form): 
    comment = forms.CharField(
            widget=forms.TextInput(attrs={'size':'40'}))

Ich fand es mühsam, den nativen Widget-Typ für jedes Feld kennen zu müssen, und fand es lustig, die Standardeinstellung zu überschreiben, nur um einen Klassennamen in ein Formularfeld einzufügen. Das scheint bei mir zu funktionieren:

class MyForm(forms.Form): 
    #This instantiates the field w/ the default widget
    comment = forms.CharField()

    #We only override the part we care about
    comment.widget.attrs['size'] = '40'

Das scheint mir ein bisschen sauberer zu sein.

Dave
quelle
Dies ist genau das, was andere vor langer Zeit gesagt haben, außer dass Sie es mit "Größe" und nicht mit "Klasse" tun, was angefordert wurde.
Chris Morgan
3
@Chris Morgan Eigentlich ist er der erste, der vorschlägt, "comment.widget.attrs" in der Formulardeklaration selbst zu verwenden (anstatt mit init herumzuspielen oder dies in der Ansicht zu tun).
DarwinSurvivor
29

Wenn alle Felder im Formular eine bestimmte Klasse erben sollen, definieren Sie einfach eine übergeordnete Klasse, die von dieser erbt forms.ModelFormund dann von ihr erbt

class BaseForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(BaseForm, self).__init__(*args, **kwargs)
        for field_name, field in self.fields.items():
            field.widget.attrs['class'] = 'someClass'


class WhateverForm(BaseForm):
    class Meta:
        model = SomeModel

Dies hat mir geholfen, die 'form-control'Klasse automatisch zu allen Feldern in allen Formularen meiner Anwendung hinzuzufügen, ohne die Replikation von Code hinzuzufügen.

Oleg Belousov
quelle
1
Bitte beginnen Sie Klassennamen mit Großbuchstaben (auch BaseForm scheint ein viel besserer Name dafür zu sein)
Alvaro
16

Hier ist eine einfache Möglichkeit, die Ansicht zu ändern. Fügen Sie unten in der Ansicht hinzu, bevor Sie es in die Vorlage übergeben.

form = MyForm(instance = instance.obj)
form.fields['email'].widget.attrs = {'class':'here_class_name'}
Spider Man
quelle
14

Fügen Sie die Klassen einfach wie folgt zu Ihrem Formular hinzu.

class UserLoginForm(forms.Form):
    username = forms.CharField(widget=forms.TextInput(
        attrs={
        'class':'form-control',
        'placeholder':'Username'
        }
    ))
    password = forms.CharField(widget=forms.PasswordInput(
        attrs={
        'class':'form-control',
        'placeholder':'Password'
        }
    ))
paulc
quelle
5

Hier ist eine Variation der obigen, die allen Feldern die gleiche Klasse gibt (z. B. schöne, abgerundete Ecken).

  # Simple way to assign css class to every field
  def __init__(self, *args, **kwargs):
    super(TranslatedPageForm, self).__init__(*args, **kwargs)
    for myField in self.fields:
      self.fields[myField].widget.attrs['class'] = 'ui-state-default ui-corner-all'
timlinux
quelle
5

Sie können dies versuchen ..

class SampleClass(forms.Form):
  name = forms.CharField(max_length=30)
  name.widget.attrs.update({'class': 'your-class'})
...

Weitere Informationen finden Sie unter: Django Widgets

Andres Quiroga
quelle
2

Wenn Sie dem Feld eines Formulars in einer Vorlage eine Klasse hinzufügen möchten (nicht in view.py oder form.py), z. B. in Fällen, in denen Sie Apps von Drittanbietern ändern möchten, ohne deren Ansichten zu überschreiben, dann einen Vorlagenfilter wie beschrieben in Charlesthk Antwort ist sehr bequem. In dieser Antwort überschreibt der Vorlagenfilter jedoch alle vorhandenen Klassen, die das Feld möglicherweise hat.

Ich habe versucht, dies als Bearbeitung hinzuzufügen, aber es wurde vorgeschlagen, es als neue Antwort zu schreiben.

Hier ist also ein Vorlagen-Tag, das die vorhandenen Klassen des Felds berücksichtigt:

from django import template

register = template.Library()


@register.filter(name='addclass')
def addclass(field, given_class):
    existing_classes = field.field.widget.attrs.get('class', None)
    if existing_classes:
        if existing_classes.find(given_class) == -1:
            # if the given class doesn't exist in the existing classes
            classes = existing_classes + ' ' + given_class
        else:
            classes = existing_classes
    else:
        classes = given_class
    return field.as_widget(attrs={"class": classes})
dimyG
quelle
Danke für Ihren Vorschlag. Ich habe meine Antwort entsprechend mit einem "pythonischeren" Ansatz bearbeitet.
Charlesthk
toll !!, das habe ich gesucht
ugali soft
1

Wie sich herausstellt, können Sie dies im Formularkonstruktor ( Init- Funktion) oder nach dem Initiieren der Formularklasse tun . Dies ist manchmal erforderlich, wenn Sie kein eigenes Formular schreiben und dieses Formular von einem anderen Ort stammt -

def some_view(request):
    add_css_to_fields = ['list','of','fields']
    if request.method == 'POST':
        form = SomeForm(request.POST)
        if form.is_valid():
            return HttpResponseRedirect('/thanks/')
    else:
        form = SomeForm()

    for key in form.fields.keys():
        if key in add_css_to_fields:
            field = form.fields[key]
            css_addition = 'css_addition '
            css = field.widget.attrs.get('class', '')
            field.widget.attrs['class'] = css_addition + css_classes

    return render(request, 'template_name.html', {'form': form})
Niki.py.
quelle
1

Sie können auch Django Crispy Forms verwenden ist ein großartiges Tool zum Definieren von Formularen, falls Sie ein CSS-Framework wie Bootstrap oder Foundation verwenden möchten. Dort können Sie ganz einfach Klassen für Ihre Formularfelder angeben.

Ihre Formularklasse möchte dies dann:

from django import forms

from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Div, Submit, Field
from crispy_forms.bootstrap import FormActions

class SampleClass(forms.Form):
    name = forms.CharField(max_length=30)
    age = forms.IntegerField()
    django_hacker = forms.BooleanField(required=False)

    helper = FormHelper()
    helper.form_class = 'your-form-class'
    helper.layout = Layout(
        Field('name', css_class='name-class'),
        Field('age', css_class='age-class'),
        Field('django_hacker', css-class='hacker-class'),
        FormActions(
            Submit('save_changes', 'Save changes'),
        )
    )
Dmitrii Mikhailov
quelle