【问题标题】:Add class to form field Django ModelForm将类添加到表单字段 Django ModelForm
【发布时间】:2015-06-25 07:08:45
【问题描述】:

我正在尝试使用 Django ModelForm 编写引导表单。我已经阅读了 Django 文档Django Documentation about Forms,所以我有这个代码:

<div class="form-group">
{{ form.subject.errors }}
<label for="{{ form.subject.id_for_label }}">Email subject:</label>
{{ form.subject }}</div>

{{form.subject}}由Django渲染,例如在CharField字段模型中,作为input标签,

<input type="text"....> etc.

我需要在每个输入中添加 "form-control" 类,以便获得 Bootstrap 输入外观(无需第三方包)。我找到了这个解决方案Django add class to form <input ..> field。有没有什么办法可以在不指定Form类的每个属性的情况下,默认给每个字段添加一个类?

class ExampleForm(forms.Form):
   name = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))
   email = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))
   address = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))
   country = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))

等等..

【问题讨论】:

    标签: django django-forms django-templates


    【解决方案1】:

    如果您不能使用第三方应用程序并希望以 DRY 方式为表单中的每个字段添加一个类(例如,“form-control”),您可以在表单类@987654322 中这样做像这样的@方法:

    class ExampleForm(forms.Form):
        # Your declared form fields here
        ...
    
        def __init__(self, *args, **kwargs):
            super(ExampleForm, self).__init__(*args, **kwargs)
            for visible in self.visible_fields():
                visible.field.widget.attrs['class'] = 'form-control'
    

    您可能还需要检查 attrs 中的现有类,如果由于某种原因您将在 __init__() 中以声明方式添加类。上面的代码没有考虑这种情况。

    值得一提:

    您指定不想使用第三方软件包。不过,我会花一点时间提一下,以 Bootstrap 样式自动渲染表单的最简单方法之一是使用django-crispy-forms,如下所示:

    # settings.py
    CRISPY_TEMPLATE_PACK = 'bootstrap3'
    
    # forms.py
    from crispy_forms.helper import FormHelper
    class ExampleForm(forms.Form):
        # Your declared form fields here
        ...
        helper = FormHelper()
    
    # In your template, this renders the form Bootstrap-style:
    {% load crispy_forms_tags %}
    {% crispy form %}
    

    【讨论】:

    • 谢谢大家,我会尝试 def __init__() 解决方案,希望它有效。
    • 尝试解决方案后,不要忘记将其中一个答案标记为“已接受”,以便其他搜索相同信息的人更容易找到答案。
    • 我知道这是旧的。我尝试了第一种方法并得到了错误 - 'NoneType' 对象没有属性 'visible_fields'。
    • 酥脆的表格很棒。但是当想在 1 行上放 2 个列时,我必须在 {{field}} 中打开表单,这就是我需要手动添加一个类“表单控制”的地方
    • @Aseem 我也在尝试做同样的事情,作为一种解决方法,我最终使用 JavaScript 添加了类
    【解决方案2】:

    您可以在 forms.py 中添加 CSS 类

    subject = forms.CharField(label='subject', max_length=100 , widget=forms.TextInput(attrs={'class': "form-control"}))
    

    【讨论】:

    • 我想知道是否可以在dict 表示法中添加类。我试过attrs=dict(class='my_class'),但这显然不起作用,因为Django(即Python)在class之后期待一个类声明
    • 也许你遇到了 class 关键字的问题(因为这是保留的)使用我的语法( attrs={'class': "my_class"} )
    • ForeignKeyImageField 的小部件应该是什么样子?
    【解决方案3】:

    由于我花费了比我想要的更多的时间(django 新手)来解决这个问题,所以我也将我的结果放在这里。

    为每个字段设置小部件只是为了一遍又一遍地添加一个类,这违反了重复的编程规则,并导致许多不必要的行。在使用引导表单时尤其会发生这种情况。

    这是我的(工作)示例,不仅可以添加引导类:

    forms.py

    class CompanyForm(forms.Form):
        name = forms.CharField(label='Jméno')
        shortcut = forms.CharField(label='Zkratka')
        webpage = forms.URLField(label='Webové stránky')
        logo = forms.FileField(label='Logo')
    

    模板标签/custom_tags.py

    from django import template
    from django.urls import reverse
    
    register = template.Library()
    
    @register.filter('input_type')
    def input_type(ob):
        '''
        Extract form field type
        :param ob: form field
        :return: string of form field widget type
        '''
        return ob.field.widget.__class__.__name__
    
    
    @register.filter(name='add_classes')
    def add_classes(value, arg):
        '''
        Add provided classes to form field
        :param value: form field
        :param arg: string of classes seperated by ' '
        :return: edited field
        '''
        css_classes = value.field.widget.attrs.get('class', '')
        # check if class is set or empty and split its content to list (or init list)
        if css_classes:
            css_classes = css_classes.split(' ')
        else:
            css_classes = []
        # prepare new classes to list
        args = arg.split(' ')
        for a in args:
            if a not in css_classes:
                css_classes.append(a)
        # join back to single string
        return value.as_widget(attrs={'class': ' '.join(css_classes)})
    

    reusable_form_fields.html(模板)

    {% load custom_tags %}
    
    {% csrf_token %}
    {% for field in form %}
        <div class="form-group row">
            {% if field|input_type == 'TextInput' %}
                <div for="{{ field.label }}" class="col-sm-2 col-form-label">
                    {{ field.label_tag }}
                </div>
                <div class="col-sm-10">
                    {{ field|add_classes:'form-control'}}
                    {% if field.help_text %}
                        <small class="form-text text-muted">{{ field.help_text }}</small>
                    {% endif %}
                </div>
            {% else %}
                ...
            {% endif %}
        </div>
    {% endfor %}
    

    【讨论】:

      【解决方案4】:

      酥脆的形式是要走的路。 Bootstrap 4 的提示。添加到@Christian Abbott 的答案,对于 forms ,bootstrap 说,使用 form-group 和 form-control 。 这就是它对我的工作方式。

      我的表单.py

      class BlogPostForm(forms.ModelForm):
          class Meta:
              model = models.Post
              fields = ['title', 'text', 'tags', 'author', 'slug']
          helper = FormHelper()
          helper.form_class = 'form-group'
          helper.layout = Layout(
              Field('title', css_class='form-control mt-2 mb-3'),
              Field('text', rows="3", css_class='form-control mb-3'),
              Field('author', css_class='form-control mb-3'),
              Field('tags', css_class='form-control mb-3'),
              Field('slug', css_class='form-control'),
          )
      

      我的 post_create.html

      {% extends 'blog/new_blog_base.html' %}
      {% load crispy_forms_tags %}
      {% block content %}
      <div class="container">
      
      <form method='POST' enctype="multipart/form-data">
          {% csrf_token %}
          {{ form.media }}
          {% crispy form %}
      
      <hr>
      <input type="submit" name="Save" value="Save" class='btn btn-primary'> <a href="{% url 'home' %}" class='btn btn-danger'>Cancel</a>
      </form>
      
      </div>
      {% endblock %}
      

      注意:如果您在模型字段中使用 CK Editor RichTextField(),则该字段不会受到影响。如果有人知道,请更新此内容。

      【讨论】:

        【解决方案5】:

        这是对@Christian Abbott correct answer的补充。

        如果您使用大量表单,则不必每次都覆盖 init 的选项可能是创建自己的表单类:

        class MyBaseForm(forms.Form):
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
                for visible in self.visible_fields():
                    visible.field.widget.attrs['class'] = 'form-control'
        

        然后你可以继承这个类,它会自动为你制作样式。

        class ExampleForm(MyBaseForm):
            # Your declared form fields here
            ...
        

        通过简单地创建一个继承自 ModelForm 的 MyBaseModelForm 可以使用 ModelForm 完成同样的事情。

        【讨论】:

          【解决方案6】:

          您还可以明确提及您想要应用该类的字段

          class ProfileForm(ModelForm):
          
             class Meta:
                 model = Profile 
                  fields = ['avatar','company']  
              
              
                  def __init__(self, *args, **kwargs):
                     super().__init__(*args, **kwargs)
                     self.fields['avatar'].widget.attrs.update({'class': 'form-control'})
                     self.fields['company'].widget.attrs.update({'class':'form-control'})
          

          【讨论】:

            【解决方案7】:

            一种方法是创建基本表单类并在__init__ 方法中手动更新字段的属性。

            另一种方法是使用像这样的现有库: https://github.com/dyve/django-bootstrap3

            在 github 上有很多这样的库。环顾四周。

            【讨论】:

              【解决方案8】:

              我发现通过 css 识别元素并在那里添加样式更容易。使用 django 表单,您可以获得每个表单字段的唯一 ID(如果您在模板中多次显示表单,则为用户表单前缀)。

              # views.py
              def my_view_function(request):
                  form_a = MyForm(prefix="a")
                  form_b = MyForm(prefix="b")
                  context = {
                      "form_a": form_a,
                      "form_b": form_b
                  }
                  return render(request, "template/file.html", context)
              

              风格

              // file.css
              form input#by_id {
                width: 100%;
              }
              

              【讨论】:

                【解决方案9】:

                好的,已经过去了一段时间,但我遇到了同样的问题。我来到了这个解决方案:

                class FormCssAttrsMixin():
                    cssAttrs = {}
                
                    def inject_css_attrs(self):
                        # iterate through fields
                        for field in self.fields:
                            widget = self.fields[field].widget
                            widgetClassName = widget.__class__.__name__
                
                            # found widget which should be manipulated?
                            if widgetClassName in self.cssAttrs.keys():
                                # inject attributes
                                attrs = self.cssAttrs[widgetClassName]
                                for attr in attrs:
                                    if attr in widget.attrs:  # attribute already existing
                                        widget.attrs.update[attr] = widget[attr] + " " +    attrs[attr]  # append
                                    else:  # create attribute since its not existing yet
                                        widget.attrs[attr] = attrs[attr]
                
                
                class MyForm(FormCssAttrsMixin, forms.Form):
                    # add class attribute to all django textinputs widgets
                    cssAttrs = {"TextInput": {"class": "form-control"}}
                
                    name = forms.CharField()
                    email = forms.CharField()
                    address = forms.CharField()
                    country = forms.CharField()
                
                    def __init__(self, *args, **kwargs) -> None:
                        super().__init__(*args, **kwargs)
                
                        self.inject_css_attrs()
                

                使用这个 Mixin 类,您可以以通用方式操作表单小部件的属性。只需添加一个字典作为类变量,其中包含每个小部件所需的属性和值。 这样,您可以在定义字段的同一位置添加您的 css 类。唯一的缺点是,你必须在某个地方调用“inject_css_attrs”方法,但我认为没关系。

                【讨论】:

                  【解决方案10】:

                  这个很实用:

                  class CreateSomethingForm(forms.ModelForm):
                  
                      class Meta:
                          model = Something
                          exclude = []
                  
                      def __init__(self, *args, **kwargs):
                          super().__init__(*args, **kwargs)
                          for field in self.fields.values():
                              field.widget.attrs['class'] = 'form-control'
                  

                  这样你就不必逐个地去。

                  【讨论】:

                    【解决方案11】:

                    我知道作者询问了自己表单的 Bootstrap,但是还有另一种方法可以在 Django 表单中包含 Bootstrap 类标签以进行身份​​验证、密码重置等。

                    如果我们用标准格式创建模板:

                    <form action="" method="post">
                       {% csrf_token %}
                       {{ form }}
                    </form>
                    

                    然后在浏览器源代码中我们可以看到所有带有标签的表单字段:

                    <form action="" method="post">
                    <input type="hidden" name="csrfmiddlewaretoken" value="xxx">
                        <tr><th><label for="id_old_password">Old password:</label></th><td><input type="password" name="old_password" autofocus required id="id_old_password"></td></tr>
                        <tr><th><label for="id_new_password1">New password:</label></th><td><input type="password" name="new_password1" required id="id_new_password1"></td></tr>
                        <tr><th><label for="id_new_password2">New password confirmation:</label></th><td><input type="password" name="new_password2" required id="id_new_password2"></td></tr>
                    </form>
                    

                    我们模板中的变量{{ form }}现在可以替换为我们需要的代码和引导类:

                    <div class="fieldWrapper form-group" aria-required="true">
                        <label for="id_old_password">Old password:</label><span class="required">*</span>
                        <input type="password" **class="form-control"** name="old_password" autofocus required id="id_old_password">
                    </div>
                    

                    也许它对重新设计内置静态表单很有用。

                    【讨论】:

                    • 这是非常错误的,您将失去 1) 服务器验证反馈、2) 提示、3) 对模型的更改 4) 可能是安全性 5) 它不灵活 6) 它不可维护 7) 等等。 ..
                    猜你喜欢
                    • 2018-03-14
                    • 2011-07-14
                    • 2015-08-31
                    • 2012-02-17
                    • 2011-05-19
                    • 2011-06-07
                    • 2011-01-05
                    • 2011-08-29
                    相关资源
                    最近更新 更多