Django表单中的CSS样式

183

我想要给以下内容添加样式:

forms.py:

from django import forms

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    email = forms.EmailField(required=False)
    message = forms.CharField(widget=forms.Textarea)

contact_form.html:

<form action="" method="post">
  <table>
    {{ form.as_table }}
  </table>
  <input type="submit" value="Submit">
</form>
例如,我如何为主题、电子邮件、消息设置一个class或ID以提供外部样式表?

例如,我如何为主题、电子邮件和消息设置class或ID以便提供外部样式表?

17个回答

246

以下是我对这个问题的回答:

如何在Django中使用
标记表单字段
class MyForm(forms.Form):
    myfield = forms.CharField(widget=forms.TextInput(attrs={'class': 'myfieldclass'}))

或者

class MyForm(forms.ModelForm):
    class Meta:
        model = MyModel

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.fields['myfield'].widget.attrs.update({'class': 'myfieldclass'})

或者

class MyForm(forms.ModelForm):
    class Meta:
        model = MyModel
        widgets = {
            'myfield': forms.TextInput(attrs={'class': 'myfieldclass'}),
        }

---编辑---
上述是对原问题代码进行的最简单更改,以实现所要求的功能。如果您在其他地方重复使用表单,则可以避免重复自己;如果您使用Django的as_table/as_ul/as_p表单方法,则您的类或其他属性将正常工作。如果您需要完全自定义渲染,请参阅清楚记录的内容。

-- 编辑2 ---
添加了一种新的指定ModelForm小部件和属性的方法。


35
虽然不建议将演示和业务逻辑混合。 - Torsten Engelbrecht
9
这个演示文稿怎么样?你正在为该元素添加一个类,这只是一种标识符或分类方式。你仍然需要在其他地方定义它的作用。 - shadfc
9
是与否。首先,CSS类通常按约定用于样式,如果需要唯一标识符,则最好使用id。其次,通常模板方的责任是确切地做到这一点,尤其是如果您将通过前端方法(js、css)访问此类。我并没有说你的答案是错误的。在我看来,这只是一种不良实践(特别是当你和前端、后端开发人员一起工作时)。 - Torsten Engelbrecht
9
这看起来很荒谬,只为了添加一个类需要这么多代码吗?对于一个 CSS 重的网站,在这些区域中硬编码 HTML/CSS 似乎更容易。 - David542
17
Django让这件事情变得如此困难,真是疯狂! - Bryce
显示剩余11条评论

141

您可以使用自定义模板过滤器来完成此操作。考虑以这种方式呈现您的表单:

<form action="/contact/" method="post">
  {{ form.non_field_errors }}
  <div class="fieldWrapper">
    {{ form.subject.errors }}
    {{ form.subject.label_tag }}
    {{ form.subject }}
    <span class="helptext">{{ form.subject.help_text }}</span>
  </div>
</form>

form.subjectBoundField 的一个实例,它有 as_widget() 方法。

你可以在 my_app/templatetags/myfilters.py 中创建一个自定义过滤器 addclass

from django import template

register = template.Library()

@register.filter(name='addclass')
def addclass(value, arg):
    return value.as_widget(attrs={'class': arg})

然后应用您的筛选器:

{% load myfilters %}

<form action="/contact/" method="post">
  {{ form.non_field_errors }}
  <div class="fieldWrapper">
    {{ form.subject.errors }}
    {{ form.subject.label_tag }}
    {{ form.subject|addclass:'MyClass' }}
    <span class="helptext">{{ form.subject.help_text }}</span>
  </div>
</form>

然后,form.subjects 将使用 MyClass CSS 类进行呈现。


9
这是较为简洁易于实施的解决方案之一。 - user
6
这个答案应该是最佳答案!!!它比Django提供的解决方案更加简洁!做得好@Charlesthk - David Dahan
6
非常有用。一开始我并没有意识到,但是你也可以使用这个方法来添加多个类:{{ form.subject|addclass:'myclass1 myclass2' }} - smg
我喜欢这种方法可以让HTML类保留在HTML文件中。在处理样式时,我会在样式表和结构之间来回跳转,而不是模型和/或表单。 - Kevin
1
这种方法的一个问题是,该过滤器将BoundField转换为SafeString,所以无法链式调用其他(类似的)过滤器。 django-widget-tweaks返回字段,因此它是更强大的解决方案。 - minusf
我可以确认这种方法适用于使用Django 3.2的Jinja2(无需注册装饰器)。 - xax

34

如果您不想向表单中添加任何代码(如@shadfc答案中的评论所述),那么肯定是有可能的,这里有两个选择。

首先,您只需在HTML中单独引用字段,而不是一次性引用整个表单:

<form action="" method="post">
    <ul class="contactList">
        <li id="subject" class="contact">{{ form.subject }}</li>
        <li id="email" class="contact">{{ form.email }}</li>
        <li id="message" class="contact">{{ form.message }}</li>
    </ul>
    <input type="submit" value="Submit">
</form>

请注意,我还将其更改为未排序的列表

其次,在有关以HTML输出表单的文档中,Django:

字段id是通过在字段名称前添加“id_”生成的。默认情况下,id属性和标签包含在输出中。

您的所有表单字段已经具有唯一的id。因此,您可以在CSS文件中引用id_subject来样式化subject字段。值得注意的是,这是当您采用默认 HTML时表单的行为,这只需要打印表单而不是单个字段:

<ul class="contactList">
    {{ form }}  # Will auto-generate HTML with id_subject, id_email, email_message 
    {{ form.as_ul }} # might also work, haven't tested
</ul>

请参见前面的链接,以了解在输出表单时的其他选项(您可以制作表格等)。
注意 - 我意识到这与为每个元素添加类不同(如果您向表单添加字段,则还需要更新CSS)- 但是在CSS中通过id引用所有字段非常容易,如下所示:
#id_subject, #id_email, #email_message 
{color: red;}

我尝试了你的第二个解决方案,但它没有起作用。我为id_email创建了一个类,但它未能产生任何结果。 - almost a beginner
@almostabeginner 我可以建议你一个调试的方法 - 一旦你在浏览器中看到页面,使用“查看页面源代码”(通常通过右键单击),查看Django生成的实际完整页面。看看字段是否存在,并且具有您期望的_id_或_class_标识符。此外,大多数浏览器(可能通过安装插件)可以运行调试器,显示应用于页面的_css_,这也有助于查看发生了什么。 - John C
@almostabeginner 还要注意一下,我添加了一些样本代码。如果仅从文本中无法清楚地理解 - 您必须引用表单本身,而不是单个字段,此时表单会自动生成包含_ids_的HTML,如所述。希望这可以帮助您。 - John C
1
感谢您的帮助,问题并不在我的CSS上,而是与缓存有关。因此,我的旧CSS被存储了下来,因此没有任何更改会显示出来。我只需清除Chrome中的缓存,所有更新就开始显示了。 - almost a beginner

28

根据这篇博客文章,您可以使用自定义模板过滤器为字段添加CSS类。

from django import template
register = template.Library()

@register.filter(name='addcss')
def addcss(field, css):
    return field.as_widget(attrs={"class":css})
将此内容放入您应用程序的templatetags/文件夹中,然后您现在可以执行以下操作:
{{field|addcss:"form-control"}}

4
这应该被接受为这篇文章的真实答案 :) - mvdb
1
迄今为止最佳解决方案。 - Mods Vs Rockers
1
太棒了,谢谢!别忘了实际加载标签。另外,在Django 2.1中,我能让Django找到模板的唯一方法是在settings.py中添加一个选项:'libraries':{ 'add_css': 'app.templatetags.tag_name', } - simonbogarde
这绝对是最好的解决方案。出于教育目的,应该将其移动到被接受的答案中! - paulc
博客链接已失效。 - Dr Phil
显示剩余2条评论

14

您可以这样做:

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    subject.widget.attrs.update({'id' : 'your_id'})

希望那能起作用。

Ignas


谢谢Ignas。回答非常准确! - Rudresh Ajgaonkar

10

1
看看Charlesthk的解决方案,它是相同的,而不需要添加额外的库 :) - David Dahan
@DavidW.:是的,但Widget Tweaks有更多的过滤器,比如 render_field - mrdaliri
Charlesthk的解决方案将BoundField转换为SafeString,因此其他(类似的)过滤器无法链接。django-widget-tweaks返回字段,因此它是一个更强大的解决方案。 - minusf

7

请按以下方式编写表单:

    class MyForm(forms.Form):
         name = forms.CharField(widget=forms.TextInput(attr={'class':'name'}),label="Your Name")
         message = forms.CharField(widget=forms.Textarea(attr={'class':'message'}), label="Your Message")

在你的 HTML 领域中,可以这样做:
{% for field in form %}
      <div class="row">
        <label for="{{ field.name}}">{{ field.label}}</label>{{ field }}
     </div>
{% endfor %}

然后在你的 CSS 中写入类似以下代码:

.name{
      /* you already have this class so create it's style form here */
}
.message{
      /* you already have this class so create it's style form here */
}
label[for='message']{
      /* style for label */
}

希望这个答案值得一试!注意,您必须编写您的观点以呈现包含表单的HTML文件。

谢谢。但是我该如何编写特定标签的文本? - msahin
1
TypeError: init() got an unexpected keyword argument 'attr',在Django 3.x的文件字段上无法正常工作。 - vipul petkar

5

你可以做:

<form action="" method="post">
    <table>
        {% for field in form %}
        <tr><td>{{field}}</td></tr>
        {% endfor %}
    </table>
    <input type="submit" value="Submit">
</form>

然后,您可以为例如 <td> 标签添加类/ID。当然,您可以使用任何其他标签。查看与Django表单一起工作作为示例,了解表单中每个field可用的内容(例如,{{field}} 只输出输入标记,而不是标签等)。


3

没看到这个...

https://docs.djangoproject.com/en/1.8/ref/forms/api/#more-granular-output

更精细的输出

as_p()、as_ul() 和 as_table() 方法只是懒惰开发者的快捷方式,它们不是显示表单对象的唯一方式。

class BoundField 用于显示表单实例的单个字段的 HTML 或访问属性。

此对象的 str() 方法(在 Python 2 中为 unicode)显示此字段的 HTML。

要检索单个 BoundField,请使用字典查找语法,在您的表单上使用字段名称作为键:

>>> form = ContactForm()
>>> print(form['subject'])
<input id="id_subject" type="text" name="subject" maxlength="100" />

要检索所有BoundField对象,请遍历表单:

>>> form = ContactForm()
>>> for boundfield in form: print(boundfield)
<input id="id_subject" type="text" name="subject" maxlength="100" />
<input type="text" name="message" id="id_message" />
<input type="email" name="sender" id="id_sender" />
<input type="checkbox" name="cc_myself" id="id_cc_myself" />

领域特定的输出遵循表单对象的auto_id设置:
>>> f = ContactForm(auto_id=False)
>>> print(f['message'])
<input type="text" name="message" />
>>> f = ContactForm(auto_id='id_%s')
>>> print(f['message'])
<input type="text" name="message" id="id_message" />

3

一种解决方案是在页面加载完毕后使用JavaScript添加所需的CSS类。例如,使用Bootstrap类样式化Django表单输出(出于简洁起见使用jQuery):

<script type="text/javascript">
    $(document).ready(function() {
        $('#some_django_form_id').find("input[type='text'], select, textarea").each(function(index, element) {
            $(element).addClass("form-control");
        });
    });
</script>

这样就避免了将样式细节与业务逻辑混合的丑陋情况。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接