กำหนดคลาส CSS ในแบบฟอร์ม django


192

สมมติว่าฉันมีแบบฟอร์ม

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

มีวิธีที่ฉันจะกำหนดคลาส CSS ในแต่ละเขตข้อมูลที่ฉันสามารถใช้ jQuery ตามชั้นเรียนในหน้าการแสดงผลของฉันได้หรือไม่

ฉันหวังว่าจะไม่ต้องสร้างแบบฟอร์มด้วยตนเอง


9
ว้าวทุกคนขึ้นคะแนนนี้? docs.djangoproject.com/en/dev/ref/forms/widgets/…
Skylar Saveland

10
@skyl ฉันจะใช้เป็นตัวบ่งชี้ว่ามันไม่ง่ายที่จะหาใน django docs ฉันเรียกดูและค้นหา google หลายครั้งด้วยและไม่พบสิ่งนี้ดังนั้นฉันดีใจที่มีคำถามและได้รับอัปโหลดของฉัน
นิลส์

ฉันรู้ว่ามันเป็นเธรดเก่า แต่ในเขตข้อมูลฟอร์ม Django ตอนนี้มีคลาส id_fieldname
ratsimihah

1
ดูคำตอบเกี่ยวกับวิธีกำหนดคลาสสำหรับฟิลด์ฟอร์มภายในเทมเพลตขณะที่เคารพคลาสของฟิลด์ที่มีอยู่
dimyG

คำตอบ:


182

แต่วิธีอื่นที่ไม่ต้องมีการเปลี่ยนแปลงในรหัสหลามและเพื่อให้เป็นที่ดีสำหรับนักออกแบบและ one-off เปลี่ยนแปลง presentational: Django-เครื่องมือ-ปรับแต่ง หวังว่าใครบางคนจะพบว่ามีประโยชน์


61
เพียงการแก้ปัญหามีสติผมต้องบอกว่า ขอบคุณ!. รหัส Python และโดยเฉพาะอย่างยิ่งในคำจำกัดความของแบบฟอร์มเป็นสถานที่สุดท้ายที่จะนำสิ่งของมาใส่สไตล์ - เหล่านี้เป็นของเทมเพลตแน่นอน
Boris Chervenkov

5
นี่เป็นห้องสมุดที่ยอดเยี่ยม! น่าเสียดายที่คำตอบนี้ถูกฝังอยู่ที่ด้านล่าง
Chris Lacasse

1
ยอดเยี่ยมสำหรับนักออกแบบ! :-)
hobbes3

1
ยอดเยี่ยม ฉันได้พัฒนาตัวกรองเพื่อทำสิ่งเหล่านี้ แต่โครงการนี้มีประสิทธิภาพมากกว่า ขอบคุณ!
msbrogli

1
จริงๆแล้วมันใช้งานได้กับ Jinja2 ด้วย :-) ฉันเปลี่ยนลำดับของ fileter ที่ปลอดภัยและเพิ่มวงเล็บแทนเครื่องหมายโคลอน {{myform.email | add_class ("css_class_1 css_class_2") | safe}} ขอบคุณสำหรับการเขียนนี้ มันควรเป็นส่วนหนึ่งของ Django
David Dehghan

92

ตอบคำถามของฉันเอง ถอนหายใจ

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

ฉันไม่รู้ว่ามันถูกส่งไปยังตัวสร้างวิดเจ็ต


2
นั่นหมายความว่ามันไม่สามารถใช้กับคลาส ModelForm ได้หรือไม่?
xster

2
ไม่คุณเพียงแค่ต้องกำหนดเขตข้อมูลฟอร์มในแบบจำลองอย่างชัดเจนเพื่อให้คุณสามารถกำหนดวิดเจ็ต หรือใช้โซลูชันที่แสดงด้านล่างเพื่อหลีกเลี่ยงการโคลนด้วยฟิลด์ฟอร์ม
Tom

78

นี่เป็นอีกวิธีในการเพิ่มคำจำกัดความของคลาสลงในวิดเจ็ตหลังจากประกาศเขตข้อมูลในชั้นเรียน

def __init__(self, *args, **kwargs):
    super(SampleClass, self).__init__(*args, **kwargs)
    self.fields['name'].widget.attrs['class'] = 'my_class'

4
สำหรับ ModelForms สิ่งนี้มักจะดีกว่าเพราะคุณไม่จำเป็นต้องทราบถึงเขตข้อมูลฟอร์มเริ่มต้นที่ใช้อยู่และคุณสามารถตั้งค่าคลาสที่แตกต่างกันแบบไดนามิกตามเงื่อนไขรันไทม์ ทำความสะอาดได้ดีกว่าการเข้ารหัส meta hacks ...
Daniel Naab

1
สมมติว่าคุณต้องการอินพุตสำหรับทุกฟิลด์ในฟอร์มซึ่งมักจะไม่ใช่กรณี แบบฟอร์มไม่จำเป็นต้องเชื่อมโยงกับแบบจำลอง - มันอาจเชื่อมโยงกับแบบจำลองหลายแบบ ในกรณีที่เขตข้อมูลโมเดลและเขตข้อมูลฟอร์มมีความสัมพันธ์แบบ 1: 1 ดังนั้นใช่ ModelForm เป็นตัวเลือกที่ดีกว่ามาก
เล่นแร่แปรธาตุ

44

ใช้django-widget-tweaksมันใช้งานง่ายและใช้งานได้ดี

มิฉะนั้นคุณสามารถทำได้โดยใช้ตัวกรองแม่แบบกำหนดเอง

พิจารณาว่าคุณสร้างแบบฟอร์มของคุณด้วยวิธีนี้:

<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 เป็นตัวอย่างของBoundFieldซึ่งมีเมธอดas_widget

คุณสามารถสร้างตัวกรองที่กำหนดเอง "addcss" ใน "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})

จากนั้นใช้ตัวกรองของคุณ:

{% 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 จะแสดงผลด้วยคลาส "MyClass" css

หวังว่าความช่วยเหลือนี้

แก้ไข 1

  • อัปเดตตัวกรองตามคำตอบของdimyG

  • เพิ่มลิงค์ django-widget-tweak

แก้ไข 2

  • อัปเดตตัวกรองตามความคิดเห็นของBhyd

3
มันเยี่ยมมาก! เป็น DRY และแยกเลเยอร์การแสดงผลออกจากเลเยอร์ควบคุม +1 จากฉัน!
alekwisnia

1
อย่างไรก็ตามตอนนี้ฉันสังเกตเห็นข้อเสียเปรียบหนึ่งข้อ ถ้าฉันกำหนดคลาสในวิดเจ็ต attrs พวกเขาจะถูกแทนที่ด้วยตัวกรอง 'addcss' นี้ คุณมีความคิดใด ๆ ที่จะผสานมันได้อย่างไร
alekwisnia

1
ฉันหมายถึง as_widget () แทนที่ attrs วิธีการตรวจสอบให้แน่ใจว่ามันใช้ attrs ที่มีอยู่และขยายด้วยใหม่
alekwisnia

ดูคำตอบนี้เกี่ยวกับวิธีการเคารพคลาสของฟิลด์ที่มีอยู่
dimyG

get('class', None).split(' ')จะล้มเหลวถ้าแท็กไม่ได้มีแอตทริบิวต์ระดับที่เป็นNoneจะถูกส่งกลับ เปลี่ยนเป็นใช้get('class', '').split(' ')งานได้
dtasev

32

การขยายตัวของวิธีการชี้ไปที่ docs.djangoproject.com:

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

ฉันคิดว่าเป็นเรื่องลำบากที่จะต้องรู้ประเภทวิดเจ็ตเนทีฟสำหรับทุกฟิลด์และคิดว่ามันตลกที่จะแทนที่ค่าเริ่มต้นเพียงใส่ชื่อคลาสในฟิลด์ฟอร์ม ดูเหมือนว่าจะใช้งานได้สำหรับฉัน:

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'

ดูเหมือนว่าฉันจะสะอาดกว่านี้เล็กน้อย


นี่คือสิ่งที่คนอื่นพูดเมื่อหลายปีก่อนยกเว้นว่าคุณทำด้วย 'ขนาด' แทนที่จะเป็น 'คลาส' ที่ถูกร้องขอ
Chris Morgan

3
@ Chris Morgan ที่จริงแล้วเขาเป็นคนแรกที่แนะนำให้ใช้ "comment.widget.attrs" ในแบบฟอร์มการประกาศตัวเอง (แทนที่จะยุ่งกับinitหรือทำในมุมมอง)
DarwinSurvivor

29

ถ้าคุณต้องการให้เขตข้อมูลทั้งหมดในฟอร์มสืบทอดคลาสที่แน่นอนคุณเพียงกำหนดคลาสพาเรนต์ที่สืบทอดจากforms.ModelFormนั้นสืบทอดมาจากคลาสนั้น

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

สิ่งนี้ช่วยให้ฉันเพิ่ม'form-control'คลาสลงในฟิลด์ทั้งหมดในทุกรูปแบบของแอปพลิเคชันของฉันโดยอัตโนมัติโดยไม่ต้องเพิ่มการจำลองแบบของรหัส


1
โปรดเริ่มต้นชื่อชั้นเรียนด้วยตัวพิมพ์ใหญ่ (เช่น BaseForm ดูเหมือนชื่อที่ดีกว่ามากสำหรับมัน)
Alvaro

16

นี่คือวิธีง่าย ๆ ในการแก้ไขในมุมมอง เพิ่มด้านล่างในมุมมองก่อนส่งผ่านไปยังเทมเพลต

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

14

เพียงเพิ่มคลาสในแบบฟอร์มของคุณดังต่อไปนี้

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

5

นี่คือความแตกต่างด้านบนซึ่งจะทำให้ฟิลด์ทั้งหมดมีคลาสเดียวกัน (เช่น jQuery nice round corner)

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

5

คุณสามารถลองสิ่งนี้ ..

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

คุณสามารถดูข้อมูลเพิ่มเติมได้ที่: Django Widgets


2

ในกรณีที่คุณต้องการเพิ่มคลาสในฟิลด์ของแบบฟอร์มในเทมเพลต (ไม่ใช่ใน view.py หรือ form.py) ตัวอย่างเช่นในกรณีที่คุณต้องการแก้ไขแอปของบุคคลที่สามโดยไม่มีการแทนที่มุมมองจากนั้นตัวกรองเทมเพลตดังอธิบาย ในคำตอบของCharlesthk สะดวกมาก แต่ในคำตอบนี้ตัวกรองเทมเพลตจะแทนที่คลาสที่มีอยู่ซึ่งฟิลด์นั้นอาจมี

ฉันพยายามเพิ่มสิ่งนี้เป็นการแก้ไข แต่แนะนำให้เขียนเป็นคำตอบใหม่

ดังนั้นนี่คือเทมเพลตแท็กที่เคารพคลาสที่มีอยู่ของฟิลด์:

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

ขอบคุณสำหรับคำแนะนำของคุณ ฉันแก้ไขคำตอบด้วยวิธี "pythonic" ที่มากขึ้น
Charlesthk

ยอดเยี่ยม !! นั่นคือสิ่งที่ฉันกำลังมองหา
ugali soft

1

เมื่อปรากฎว่าคุณสามารถทำสิ่งนี้ได้ใน form constructor ( ฟังก์ชั่นinit ) หรือหลังจากคลาสฟอร์มเริ่มต้นแล้ว บางครั้งจำเป็นต้องมีหากคุณไม่ได้เขียนแบบฟอร์มของคุณเองและแบบฟอร์มนั้นมาจากที่อื่น -

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

1

คุณสามารถใช้Django Crispy Formsเป็นเครื่องมือที่ยอดเยี่ยมในการกำหนดรูปแบบในกรณีที่คุณต้องการใช้เฟรมเวิร์ก CSS บางอย่างเช่น Bootstrap หรือ Foundation และง่ายต่อการระบุคลาสสำหรับเขตข้อมูลฟอร์มของคุณที่นั่น

ชั้นเรียนแบบฟอร์มของคุณต้องการสิ่งนี้:

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'),
        )
    )
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.