Tentukan kelas css dalam Formulir Django

192

Anggaplah saya memiliki formulir

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

Apakah ada cara bagi saya untuk mendefinisikan kelas css pada masing-masing bidang sehingga saya dapat menggunakan jQuery berdasarkan kelas di halaman yang diberikan?

Saya berharap tidak harus secara manual membangun formulir.

ashchristopher
sumber
9
wow, semuanya pilih ini? docs.djangoproject.com/en/dev/ref/forms/widgets/...
Skylar Saveland
10
@ skyl Saya akan menganggapnya sebagai indikasi bahwa tidak mudah ditemukan di django docs. Saya melihat-lihat dan melakukan beberapa pencarian google juga dan tidak dapat menemukan ini, jadi saya senang dengan pertanyaannya dan ini membuat saya merasa senang.
Nils
Saya tahu ini adalah utas lama tetapi di bidang formulir Django sekarang memiliki kelas id_fieldname.
ratsimihah
1
Lihat jawaban ini tentang cara mendefinisikan kelas untuk bidang formulir di dalam Templat sambil menghormati kelas bidang yang ada
dimyG

Jawaban:

182

Namun solusi lain yang tidak memerlukan perubahan dalam kode python dan karena itu lebih baik untuk desainer dan perubahan presentasi satu kali: django-widget-tweaks . Semoga ada yang berguna.

Mikhail Korobov
sumber
61
Satu- satunya solusi yang waras, harus saya katakan. Terima kasih!. Kode python, dan terutama dalam definisi bentuk, adalah tempat terakhir untuk meletakkan barang-barang untuk styling - ini pasti milik template.
Boris Chervenkov
5
Ini perpustakaan yang bagus! Sayang sekali jawaban ini terkubur di bagian bawah.
Chris Lacasse
1
Bagus untuk desainer! :-)
hobbes3
1
Luar biasa! Saya telah mengembangkan beberapa filter untuk melakukan hal-hal ini, tetapi proyek ini jauh lebih kuat. Terima kasih!
msbrogli
1
sebenarnya ini bekerja untuk Jinja2 juga :-) Saya mengubah urutan fileter aman dan menambahkan tanda kurung alih-alih tanda kurung {{myform.email | add_class ("css_class_1 css_class_2") | safe}} terima kasih telah menulis ini. itu harus menjadi bagian dari Django.
David Dehghan
92

Menjawab pertanyaan saya sendiri. Mendesah

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

Saya tidak menyadari itu dilewatkan ke konstruktor widget.

ashchristopher
sumber
2
apakah itu menyiratkan itu tidak dapat digunakan dengan kelas ModelForm?
xster
2
Tidak, Anda hanya perlu mendefinisikan bidang formulir secara eksplisit dalam formulir model sehingga Anda dapat menentukan widget. Atau gunakan solusi yang tercantum di bawah ini untuk menghindari muck dengan bidang formulir.
Tom
78

Berikut adalah solusi lain untuk menambahkan definisi kelas ke widget setelah mendeklarasikan bidang di kelas.

def __init__(self, *args, **kwargs):
    super(SampleClass, self).__init__(*args, **kwargs)
    self.fields['name'].widget.attrs['class'] = 'my_class'
ashchristopher
sumber
4
Untuk ModelForms, ini seringkali lebih baik karena Anda tidak perlu menyadari bidang formulir default yang digunakan, dan Anda dapat secara dinamis mengatur kelas yang berbeda berdasarkan kondisi runtime. Lebih bersih daripada meta coding hacks
Daniel Naab
1
Itu mengasumsikan bahwa Anda menginginkan input untuk setiap bidang dalam formulir, yang seringkali tidak demikian. Suatu formulir tidak harus terikat pada model - mungkin terikat pada banyak model. Jika bidang model dan bidang formulir memiliki hubungan 1: 1, maka ya, ModelForm adalah pilihan yang jauh lebih baik.
ashchristopher
44

Gunakan django-widget-tweak , mudah digunakan dan berfungsi dengan baik.

Kalau tidak, ini bisa dilakukan menggunakan filter templat kustom.

Mengingat Anda membuat formulir Anda dengan cara ini:

<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 adalah turunan dari BoundField yang memiliki metode as_widget .

Anda dapat membuat filter khusus "addcss" di "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})

Lalu terapkan filter Anda:

{% 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 kemudian akan diberikan dengan kelas css "MyClass".

Semoga bantuan ini.

EDIT 1

  • Perbarui filter sesuai dengan jawaban dimyG

  • Tambahkan tautan django-widget-tweak

EDIT 2

  • Perbarui filter sesuai dengan komentar Bhyd
Charlesthk
sumber
3
Ini bagus! Ini KERING dan memisahkan lapisan tampilan dari lapisan kontrol. +1 dari saya!
alekwisnia
1
Namun, sekarang saya telah memperhatikan satu kekurangan. Jika saya mendefinisikan kelas di widget attrs, maka mereka ditimpa oleh filter 'addcss' ini. Apakah Anda punya ide bagaimana menggabungkannya?
alekwisnia
1
Maksud saya, as_widget () menimpa attrs. Bagaimana memastikannya menggunakan attr yang ada dan memperluasnya dengan yang baru?
alekwisnia
Lihat jawaban ini tentang cara menghormati kelas
dimyG
get('class', None).split(' ')akan gagal jika tag tidak memiliki atribut class, seperti Noneyang dikembalikan. Mengubahnya get('class', '').split(' ')berfungsi
dtasev
32

Memperluas metode yang ditunjukkan pada docs.djangoproject.com:

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

Saya pikir itu merepotkan karena harus mengetahui jenis widget asli untuk setiap bidang, dan menganggapnya lucu untuk mengesampingkan default hanya dengan meletakkan nama kelas pada bidang formulir. Ini sepertinya bekerja untuk saya:

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'

Ini agak bersih bagi saya.

Dave
sumber
Inilah tepatnya yang dikatakan orang lain, berabad-abad yang lalu, kecuali bahwa Anda melakukannya dengan 'ukuran' daripada 'kelas' yang diminta.
Chris Morgan
3
@ Chris Morgan Sebenarnya, dia adalah orang pertama yang menyarankan menggunakan "comment.widget.attrs" dalam deklarasi Formulir itu sendiri (bukan main-main dengan init atau melakukannya di tampilan).
DarwinSurvivor
29

Jika Anda ingin semua bidang dalam formulir mewarisi kelas tertentu, Anda cukup mendefinisikan kelas induk, yang mewarisi dari forms.ModelForm, dan kemudian mewarisi dari itu

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

Ini membantu saya untuk menambahkan 'form-control'kelas ke semua bidang pada semua bentuk aplikasi saya secara otomatis, tanpa menambahkan replikasi kode.

Oleg Belousov
sumber
1
tolong mulai nama kelas dengan huruf besar (juga, BaseForm tampaknya nama yang jauh lebih baik untuk itu)
Alvaro
16

Berikut adalah cara sederhana untuk mengubah tampilan. tambahkan di bawah ini tepat sebelum meneruskannya ke templat.

form = MyForm(instance = instance.obj)
form.fields['email'].widget.attrs = {'class':'here_class_name'}
Manusia laba-laba
sumber
14

Cukup tambahkan kelas ke formulir Anda sebagai berikut.

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

Berikut adalah variasi di atas yang akan memberikan semua bidang kelas yang sama (misalnya sudut bulat bagus jquery).

  # 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
sumber
5

Anda bisa coba ini ..

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

Anda dapat melihat informasi lebih lanjut di: Django Widgets

Andres Quiroga
sumber
2

Jika Anda ingin menambahkan kelas ke bidang formulir di Templat (bukan di view.py atau form.py) misalnya dalam kasus yang Anda ingin memodifikasi aplikasi pihak ke-3 tanpa mengesampingkan tampilan mereka, maka filter template seperti yang dijelaskan dalam jawaban Charlesthk sangat nyaman. Tetapi dalam jawaban ini filter templat menimpa kelas yang ada yang mungkin dimiliki bidang.

Saya mencoba menambahkan ini sebagai suntingan tetapi disarankan untuk ditulis sebagai jawaban baru.

Jadi, inilah tag templat yang menghormati kelas bidang yang ada:

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
sumber
Terima kasih atas saran Anda. Saya mengedit jawaban saya sesuai dengan pendekatan yang lebih "pythonic".
Charlesthk
Hebat !!, itulah yang saya cari
ugali soft
1

Ternyata Anda bisa melakukan ini dalam bentuk konstruktor ( fungsi init ) atau setelah kelas bentuk dimulai. Ini kadang-kadang diperlukan jika Anda tidak menulis formulir Anda sendiri dan formulir itu datang dari tempat lain -

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

Anda juga bisa menggunakan Django Crispy Forms , ini adalah alat yang hebat untuk mendefinisikan formulir jika Anda ingin menggunakan beberapa kerangka kerja CSS seperti Bootstrap atau Foundation. Dan mudah untuk menentukan kelas untuk bidang formulir Anda di sana.

Kelas formulir Anda akan menyukai ini:

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
sumber