Zdefiniuj klasę css w formularzach django

192

Załóżmy, że mam formularz

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

Czy istnieje sposób, aby zdefiniować klasy css w każdym polu, dzięki czemu mogę użyć jQuery na podstawie klasy na mojej renderowanej stronie?

Miałem nadzieję, że nie będę musiał ręcznie budować formularza.

Ashchristopher
źródło
9
wow, wszyscy głosują na to? docs.djangoproject.com/en/dev/ref/forms/widgets/…
Skylar Saveland
10
@skyl Przyjmuję to jako wskazówkę, że nie jest łatwo znaleźć w dokumentach django. Przeglądałem i przeprowadzałem kilka wyszukiwań w Google i nie mogłem tego znaleźć, więc cieszę się z pytania i otrzymuję moje poparcie.
Nils
Wiem, że to stary wątek, ale w formularzach Django pola mają teraz klasę id_fieldname.
ratsimihah
1
Zobacz tę odpowiedź na temat definiowania klas dla pola formularza w szablonie, przy jednoczesnym poszanowaniu istniejących klas pól
dimyG,

Odpowiedzi:

182

Jeszcze jedno rozwiązanie, które nie wymaga zmian w kodzie Pythona, a więc jest lepsze dla projektantów i jednorazowych zmian prezentacji: django-widget-tweaks . Mam nadzieję, że ktoś uzna to za przydatne.

Michaił Korobow
źródło
61
Tylko rozsądny rozwiązanie, muszę powiedzieć. Dziękuję Ci!. Kod w języku Python, a zwłaszcza w definicji formularza, jest ostatnim miejscem do umieszczania elementów do stylizacji - zdecydowanie należą one do szablonów.
Boris Chervenkov
5
To świetna biblioteka! Szkoda, że ​​odpowiedź jest zakopana na dole.
Chris Lacasse,
1
Idealne dla projektantów! :-)
hobbes3
1
Doskonały! Opracowałem kilka filtrów do tych rzeczy, ale ten projekt jest znacznie potężniejszy. Dzięki!
msbrogli
1
faktycznie działa również dla Jinja2 :-) Zmieniłem kolejność bezpiecznego filtrowania i dodałem nawias zamiast dwukropka {{myform.email | add_class ("css_class_1 css_class_2") | safe}} dziękuję za napisanie tego. powinien być częścią Django.
David Dehghan
92

Odpowiedziałem na własne pytanie. Westchnienie

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

Nie wiedziałem, że został przekazany do konstruktora widgetów.

Ashchristopher
źródło
2
czy to oznacza, że ​​nie można go używać z klasą ModelForm?
xster
2
Nie, wystarczy jawnie zdefiniować pole formularza w formularzu modelu, aby można było zdefiniować widżet. Lub skorzystaj z rozwiązania wymienionego poniżej, aby uniknąć konieczności zmiany pola formularza.
Tom
78

Oto inne rozwiązanie do dodawania definicji klas do widżetów po zadeklarowaniu pól w klasie.

def __init__(self, *args, **kwargs):
    super(SampleClass, self).__init__(*args, **kwargs)
    self.fields['name'].widget.attrs['class'] = 'my_class'
Ashchristopher
źródło
4
W przypadku ModelForms jest to często lepsze, ponieważ nie trzeba być świadomym użycia domyślnego pola formularza i można dynamicznie ustawiać różne klasy na podstawie warunków środowiska wykonawczego. Czystsze niż hackowanie meta ...
Daniel Naab
1
Zakłada się, że potrzebujesz danych wejściowych dla każdego pola w formularzu, co często nie jest prawdą. Formularz niekoniecznie jest powiązany z modelem - może być powiązany z wieloma modelami. W przypadku, gdy pola modelu i pola formularza mają relację 1: 1, to tak, ModelForm jest znacznie lepszym wyborem.
ashchristopher
44

Użyj ulepszeń django-widget- , jest łatwy w użyciu i działa całkiem dobrze.

W przeciwnym razie można to zrobić za pomocą niestandardowego filtru szablonu.

Biorąc pod uwagę, że renderujesz formularz w ten sposób:

<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 jest instancją BoundField, która ma metodę as_widget .

możesz utworzyć niestandardowy filtr „addcss” w „my_app / templatetags / myfilters.py”

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

A następnie zastosuj filtr:

{% 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 będą następnie renderowane za pomocą klasy css „MyClass”.

Mam nadzieję, że to pomoże.

EDYCJA 1

  • Aktualizacja filtru według dimyG „s odpowiedź

  • Dodaj link do django-widget-tweak

EDYCJA 2

  • Zaktualizuj filtr zgodnie z komentarzem Bhyd
Charlesthk
źródło
3
To jest świetne! Jest SUCHY i oddziela warstwę wyświetlacza od warstwy kontrolnej. +1 ode mnie!
alekwisnia
1
Jednak teraz zauważyłem jedną wadę. Jeśli zdefiniuję klasę w widgetach attrs, zostaną one zastąpione przez ten filtr „addcss”. Czy masz jakieś pomysły, jak to połączyć?
alekwisnia
1
Mam na myśli, że as_widget () przesłania attrs. Jak upewnić się, że wykorzystuje istniejące ATTR i rozszerza je o nowe?
alekwisnia
Zobacz odpowiedź na temat respektowania istniejących klas terenowych
dimyG,
get('class', None).split(' ')zakończy się niepowodzeniem, jeśli znacznik nie ma atrybutu class, tak jak Nonejest zwracany. Zmiana na get('class', '').split(' ')działającą
dtasev
32

Rozwijając metodę wskazaną na docs.djangoproject.com:

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

Pomyślałem, że trudno jest znać rodzimy typ widżetu dla każdego pola, i pomyślałem, że zabawne jest przesłonięcie domyślnej nazwy, aby umieścić nazwę klasy w polu formularza. Wydaje mi się, że to działa:

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'

Wydaje mi się to trochę czystsze.

Dave
źródło
Dokładnie tak mówili inni przed wiekami, tyle że robisz to z „rozmiarem”, a nie z „klasą”, o którą prosiliśmy.
Chris Morgan
3
@Chris Morgan Właściwie to on pierwszy zaproponował użycie „comment.widget.attrs” w samej deklaracji Form (zamiast zadzierać z init lub robić to w widoku).
DarwinSurvivor
29

Jeśli chcesz, aby wszystkie pola w formularzu dziedziczyły określoną klasę, wystarczy zdefiniować klasę nadrzędną, która dziedziczy po niej forms.ModelForm, a następnie dziedziczy po niej

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

Pomogło mi to automatycznie dodać 'form-control'klasę do wszystkich pól we wszystkich formularzach mojej aplikacji, bez dodawania replikacji kodu.

Oleg Belousov
źródło
1
proszę zacząć nazwy klas dużymi literami (również BaseForm wydaje się o wiele lepszą nazwą)
Alvaro
16

Oto prosty sposób zmiany widoku. dodaj poniżej w widoku tuż przed przekazaniem go do szablonu.

form = MyForm(instance = instance.obj)
form.fields['email'].widget.attrs = {'class':'here_class_name'}
Człowiek Pająk
źródło
14

Po prostu dodaj klasy do formularza w następujący sposób.

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'
        }
    ))
Paul
źródło
5

Oto wariant powyższego, który da wszystkim polom tę samą klasę (np. Jquery ładne zaokrąglone rogi).

  # 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
źródło
5

Możesz spróbować tego ..

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

Możesz zobaczyć więcej informacji w: Widżety Django

Andres Quiroga
źródło
2

Na przykład, jeśli chcesz dodać klasę do pola formularza w szablonie (nie w view.py lub form.py), na przykład w przypadkach, gdy chcesz zmodyfikować aplikacje innych firm bez przesłonięcia ich widoków, filtr filtra zgodnie z opisem w odpowiedzi Charlesthk jest bardzo wygodna. Ale w tej odpowiedzi filtr szablonu zastępuje wszystkie istniejące klasy, które może mieć pole.

Próbowałem dodać to jako edycję, ale zasugerowano, aby napisać jako nową odpowiedź.

Oto tag szablonu, który szanuje istniejące klasy pola:

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
źródło
Dzięki za Twoją sugestię. Odpowiednio zredagowałem swoją odpowiedź, stosując podejście bardziej „pytoniczne”.
Charlesthk
świetnie !!, właśnie tego szukałem
ugali soft
1

Jak się okazuje, można to zrobić w konstruktorze formularzy ( funkcja init ) lub po zainicjowaniu klasy formularza. Jest to czasem wymagane, jeśli nie piszesz własnego formularza, który pochodzi z innego miejsca -

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
źródło
1

Możesz także użyć Django Crispy Forms , jest to świetne narzędzie do definiowania formularzy na wypadek, gdybyś chciał użyć frameworka CSS, takiego jak Bootstrap lub Foundation. I łatwo jest tam określić klasy dla pól formularza.

Twoja klasa form chciałaby wtedy:

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
źródło