【问题标题】:CSS styling in Django formsDjango 表单中的 CSS 样式
【发布时间】:2011-08-15 05:11:57
【问题描述】:

我想设置以下样式:

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>

例如,如何为subjectemailmessage 设置classID 以提供外部样式表?

【问题讨论】:

    标签: css django django-forms


    【解决方案1】:

    摘自我的回答: How to markup form fields with <div class='field_type'> in 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 表单方法,您的类或其他属性就可以工作。如果您需要完全控制完全自定义的渲染,这是clearly documented

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

    【讨论】:

    • 虽然不建议将展示与业务逻辑混为一谈。
    • 这个演示文稿怎么样?你给元素一个类,它只是一个标识符或分类。您仍然需要定义其他地方的作用
    • 是和不是。第一个 CSS 类按惯例用于样式,如果您需要唯一标识符,最好使用id。其次,它通常由模板方负责执行此操作,特别是如果您要通过前端方法(js、css)访问此类。我没有说你的答案是错误的。在我看来,这是一种不好的做法(尤其是当您与前端和后端开发人员一起工作时)。
    • 这看起来很可笑,只是添加一个类就需要这么多代码?似乎在这些区域对 HTML/CSS 进行硬编码会更容易(尤其是对于 CSS 繁重的网站)。
    • 疯狂的 django 让这变得如此尴尬!
    【解决方案2】:

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

    <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 类呈现。

    【讨论】:

    • 这是一种更清洁且易于实施的解决方案
    • 这个答案应该是最佳答案!!!它真的比 Django 提出的解决方案更干净!干得好@Charlesthk
    • 超级有帮助。一开始对我来说并不明显,但您也可以使用它来添加多个类:{{ form.subject|addclass:'myclass1 myclass2' }}
    • 我喜欢这样可以将 HTML 类保留在 HTML 文件中。处理样式时,我在样式表和结构之间来回切换,而不是模型和/或表单。
    • 此方法的一个问题是此过滤器将BoundField 转换为SafeString,因此无法链接其他(类似)过滤器。 django-widget-tweaks 返回字段,因此它是一个更强大的解决方案。
    【解决方案3】:

    如果您不想在表单中添加任何代码(如@shadfc's Answer的cmets中所述),当然可以,这里有两个选项。

    首先,您只需在 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>
    

    (请注意,我也将其更改为未排序的列表。)

    其次,请注意outputting forms as HTML,Django 上的文档:

    字段 id,由 在字段名称前添加“id_”。 id 属性和标签是 默认包含在输出中。

    您的所有表单域都已经有一个唯一的id。因此,您将在 CSS 文件中引用 id_subject 来设置 subject 字段的样式。我应该注意,这是当您采用 default 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>
    

    输出表格时其他选项见上一个链接(可以做表格等)。

    注意 - 我意识到这与为每个元素添加一个 class 不同(如果您在表单中添加了一个字段,您还需要更新 CSS) - 但它是很容易在 CSS 中通过 id 引用所有字段,如下所示:

    #id_subject, #id_email, #email_message 
    {color: red;}
    

    【讨论】:

    • 我尝试了您的第二种解决方案,但没有奏效。我为 id_email 创建了类,但没有产生任何结果。
    • @almostabeginner 我可以建议调试的一件事 - 在浏览器中看到页面后,使用 查看页面源代码(通常通过右键单击),然后查看Django 正在生成的实际完整页面。查看字段是否存在,以及您期望的 idclass 标识符。此外,大多数浏览器(可能通过安装插件)可以运行调试器,向您显示应用于页面的 css,这也有助于查看正在发生的事情。
    • @almostabeginner 还请注意,我添加了一些示例代码。如果仅从文本中不清楚 - 您必须引用表单本身,而不是单个字段,此时表单会自动生成包含 ids 的 HTML,如所述。希望这会有所帮助。
    • 感谢您的帮助,问题根本不是我的 css,问题与缓存有关。所以我的旧 css 被存储,因此不会显示任何更改。我刚刚从 chrome 中清除了缓存,所有更新都开始显示了。
    【解决方案4】:

    根据 this 博客文章,您可以使用自定义模板过滤器将 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"}}
    

    【讨论】:

    • 这应该被接受为这篇文章的真正答案:)
    • 迄今为止最好的解决方案。
    • 太棒了,谢谢!不要忘记实际加载标签。此外,在 Django 2.1 中,我可以让 Django 找到模板的唯一方法是在 settings.py: 'libraries':{ 'add_css': 'app.templatetags.tag_name', } 中添加一个选项
    • 这确实是迄今为止最简单最好的解决方案。
    • 有关实施此解决方案的信息:docs.djangoproject.com/en/3.2/howto/custom-template-tags
    【解决方案5】:

    你可以这样做:

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

    希望有效。

    伊格纳斯

    【讨论】:

    • 谢谢 ignas。准确答案!
    【解决方案6】:

    你可以使用这个库:https://pypi.python.org/pypi/django-widget-tweaks

    它允许您执行以下操作:

    {% load widget_tweaks %}
    <!-- add 2 extra css classes to field element -->
    {{ form.title|add_class:"css_class_1 css_class_2" }}
    

    【讨论】:

    • 看一下Charlesthk的方案,不加额外库也是一样的:)
    • @DavidW.:是的,但是 Widget Tweaks 有更多过滤器,例如 render_field
    • Charlesthk 解决方案将BoundField 转换为SafeString,因此无法链接其他(类似)过滤器。 django-widget-tweaks 返回字段,因此它是一个更强大的解决方案。
    【解决方案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 文件。

    【讨论】:

    • 谢谢。但是如何才能写出具体的标签文字呢?
    • TypeError: __init__() got an unexpected keyword argument 'attr',它不适用于文件字段 django 3.x
    【解决方案8】:

    你可以这样做:

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

    然后您可以将类/ID 添加到例如&lt;td&gt; 标记。您当然可以使用您想要的任何其他标签。以Working with Django forms 为例,查看表单中每个field 可用的内容(例如{{field}} 只是输出输入标签,而不是标签等)。

    【讨论】:

      【解决方案9】:

      真的没看到这个……

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

      更精细的输出

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

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

      该对象的 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" />
      

      【讨论】:

        【解决方案10】:

        一种解决方案是在页面准备好后使用 JavaScript 添加所需的 CSS 类。例如,使用引导类设置 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>
        

        这避免了将样式细节与业务逻辑混合在一起的丑陋。

        【讨论】:

          【解决方案11】:

          您可能不需要覆盖表单类'__init__,因为 Django 在 HTML inputs 中设置了nameid 属性。你可以有这样的 CSS:

          form input[name='subject'] {
              font-size: xx-large;
          }
          

          【讨论】:

          • 添加到这个。给定“subject = forms...”,id="id_subject" 和 name="subject" 是这些属性的 Django 约定。因此,您还应该能够执行 #id_subject{ ... }
          • @solartic:你说得对,谢谢。我没有提到这一点,因为 Django 为表单集创建的 id 字段变得非常多毛……
          【解决方案12】:

          有一个非常易于安装且非常棒的 Django 工具,我使用它来进行样式设置,它可以用于每个前端框架,如 Bootstrap、Materialize、Foundation 等。它被称为 widget-tweaks 文档:Widget Tweaks

          1. 您可以将它与 Django 的通用视图一起使用
          2. 或使用您自己的表单:

          从 django 导入表单

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

          而不是使用默认值:

          {{ form.as_p }} or {{ form.as_ul }}
          

          您可以使用 render_field 属性以自己的方式对其进行编辑,该属性为您提供更类似于 html 的样式设置方式,如下例所示:

          模板.html

          {% load widget_tweaks %}
          
          <div class="container">
             <div class="col-md-4">
                {% render_field form.subject class+="form-control myCSSclass" placeholder="Enter your subject here" %}
             </div>
             <div class="col-md-4">
                {% render_field form.email type="email" class+="myCSSclassX myCSSclass2" %}
             </div>
             <div class="col-md-4">
                {% render_field form.message class+="myCSSclass" rows="4" cols="6" placeholder=form.message.label %}
             </div>
          </div>
          

          这个库让您有机会将前端与后端很好地分开

          【讨论】:

            【解决方案13】:

            在 Django 1.10(也可能更早)中,您可以按以下方式进行操作。

            型号:

            class Todo(models.Model):
                todo_name = models.CharField(max_length=200)
                todo_description = models.CharField(max_length=200, default="")
                todo_created = models.DateTimeField('date created')
                todo_completed = models.BooleanField(default=False)
            
                def __str__(self):
                    return self.todo_name
            

            表格:

            class TodoUpdateForm(forms.ModelForm):
                class Meta:
                    model = Todo
                    exclude = ('todo_created','todo_completed')
            

            模板:

            <form action="" method="post">{% csrf_token %}
                {{ form.non_field_errors }}
            <div class="fieldWrapper">
                {{ form.todo_name.errors }}
                <label for="{{ form.name.id_for_label }}">Name:</label>
                {{ form.todo_name }}
            </div>
            <div class="fieldWrapper">
                {{ form.todo_description.errors }}
                <label for="{{ form.todo_description.id_for_label }}">Description</label>
                {{ form.todo_description }}
            </div>
                <input type="submit" value="Update" />
            </form>
            

            【讨论】:

              【解决方案14】:

              编辑:这里回答了我建议的另一种(稍微好一点)的方法:Django form input field styling

              以上所有选项都很棒,只是觉得我会加入这个,因为它不同。

              如果您想在表单上自定义样式、类等,您可以在模板中输入与表单字段匹配的 html。例如,对于 CharField(默认小部件是 TextInput),假设您想要一个看起来像引导程序的文本输入。你会做这样的事情:

              &lt;input type="text" class="form-control" name="form_field_name_here"&gt;

              只要你输入的表单字段名称与 html name 属性匹配,(并且小部件可能也需要匹配输入类型)当你运行 @987654326 时,Django 将在该字段上运行所有相同的验证器@ 或 form.is_valid()

              设置标签、错误消息和帮助文本等其他内容的样式不需要太多解决方法,因为您可以执行form.field.error.as_text 之类的操作并根据需要设置它们的样式。实际的字段是需要一些摆弄的字段。

              我不知道这是否是最好的方式,或者我会推荐的方式,但这是一种方式,并且可能适合某人。

              这是样式表单的有用演练,其中包括 SO 上列出的大多数答案(例如在小部件上使用 attr 和小部件调整)。 https://simpleisbetterthancomplex.com/article/2017/08/19/how-to-render-django-form-manually.html

              【讨论】:

                【解决方案15】:

                样式化小部件实例

                如果您想让一个小部件实例看起来与另一个不同,您需要在小部件对象被实例化并分配给表单字段时指定其他属性(并且可能在您的 CSS 文件中添加一些规则)。

                https://docs.djangoproject.com/en/2.2/ref/forms/widgets/

                为此,您可以在创建小部件时使用 Widget.attrs 参数:

                class CommentForm(forms.Form):
                    name = forms.CharField(widget=forms.TextInput(attrs={'class': 'special'}))
                    url = forms.URLField()
                    comment = forms.CharField(widget=forms.TextInput(attrs={'size': '40'}))
                

                您还可以在表单定义中修改一个小部件:

                class CommentForm(forms.Form):
                    name = forms.CharField()
                    url = forms.URLField()
                    comment = forms.CharField()
                
                    name.widget.attrs.update({'class': 'special'})
                    comment.widget.attrs.update(size='40')
                

                或者如果该字段没有直接在表单上声明(例如模型表单字段),您可以使用 Form.fields 属性:

                class CommentForm(forms.ModelForm):
                    def __init__(self, *args, **kwargs):
                        super().__init__(*args, **kwargs)
                        self.fields['name'].widget.attrs.update({'class': 'special'})
                        self.fields['comment'].widget.attrs.update(size='40')
                

                然后,Django 将在渲染输出中包含额外的属性:

                >>> f = CommentForm(auto_id=False)
                >>> f.as_table()
                <tr><th>Name:</th><td><input type="text" name="name" class="special" required></td></tr>
                <tr><th>Url:</th><td><input type="url" name="url" required></td></tr>
                <tr><th>Comment:</th><td><input type="text" name="comment" size="40" required></td></tr>
                

                【讨论】:

                  【解决方案16】:

                  我一直在玩这个解决方案来保持整个应用程序的一致性:

                  def bootstrap_django_fields(field_klass, css_class):
                      class Wrapper(field_klass):
                          def __init__(self, **kwargs):
                              super().__init__(**kwargs)
                  
                          def widget_attrs(self, widget):
                              attrs = super().widget_attrs(widget)
                              if not widget.is_hidden:
                                  attrs["class"] = css_class
                              return attrs
                  
                      return Wrapper
                  
                  
                  MyAppCharField = bootstrap_django_fields(forms.CharField, "form-control")
                  

                  那么您不必逐个表单定义您的 css 类,只需使用您的自定义表单字段即可。


                  在技术上也可以像这样在启动时重新定义 Django 的 forms 类:

                  forms.CharField = bootstrap_django_fields(forms.CharField, "form-control")
                  

                  然后,您甚至可以为不受您直接控制的应用程序设置全局样式。这看起来很粗略,所以我不确定我是否可以推荐这个。

                  【讨论】:

                    【解决方案17】:

                    对于更大的形式,而不是为每个字段编写 css 分类,你可以这样做

                    class UserRegistration(forms.ModelForm):
                       # list charfields
                    
                       class Meta:
                          model = User
                          fields = ('username', 'first_name', 'last_name', 'email', 'password', 'password2')
                    
                       def __init__(self, *args, **kwargs):
                          super(UserRegistration, self).__init__(*args, **kwargs)
                          for field in self.fields:
                             self.fields[field].widget.attrs['class'] = 'form-control'
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2016-02-19
                      • 2010-12-27
                      • 2022-06-15
                      • 2012-11-02
                      • 2017-05-17
                      • 2021-01-03
                      • 2012-07-21
                      • 1970-01-01
                      相关资源
                      最近更新 更多