添加 class 到表单字段 Django ModelForm
Add class to form field Django ModelForm
我正在尝试使用 Django ModelForm 编写 Bootstrap 表单。我已经阅读了 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" class 添加到每个输入以获得 Bootstrap 输入外观(没有第三方包)。我找到了这个解决方案 Django add class to form <input ..> field。有什么方法可以在默认情况下将 class 添加到每个字段,而无需在表单 class 的 class 的每个属性中指定它?
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'}))
等等..
一种方法是创建基本表单 class 并在 __init__
方法中手动更新字段的属性。
另一种方法是使用像这样的现有库:
https://github.com/dyve/django-bootstrap3
github 周围有很多这样的图书馆。环顾四周。
如果您不能使用第三方应用程序并希望以 DRY 方式向表单中的每个字段添加 class(例如“表单控件”),您可以这样做以 class __init__()
形式的方法如下:
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'
您可能还需要处理对属性中现有 classes 的检查,如果出于某种原因您将以声明方式添加 classes 和 在 __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 %}
您可以在forms.py
中添加CSS类
subject = forms.CharField(label='subject', max_length=100 , widget=forms.TextInput(attrs={'class': "form-control"}))
松脆的表格是必经之路。 Bootstrap 的提示 4. 添加到@Christian Abbott 的回答,对于 forms ,bootstrap 说,使用 form-group 和 form-control 。
这就是它对我有用的方式。
我的forms.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(),则该字段不会受到影响。如果有人知道,请更新此内容。
我知道作者问过 Bootstrap 用于自己的表单,但是还有一种方法可以在 Django 表单中包含 Bootstrap class 标记以进行身份验证、密码重置等。
如果我们创建标准格式的模板:
<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 }}
现在可以用此代码替换 Bootstrap class 我们需要的是:
<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>
也许它对重新设计内置静态表单很有用。
因为我花了比我(django 新手)更多的时间来解决这个问题,所以我也会把我的结果放在这里。
为每个字段设置小部件只是为了一遍又一遍地添加一个 class 违反了重复的编程规则,并导致许多不必要的行。使用 bootstrap 表单时尤其会发生这种情况。
这是我的(工作)示例,不仅添加 bootstrap classes:
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')
templatetags/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 %}
我发现通过 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%;
}
好的,一段时间过去了,但我遇到了同样的问题。我来到这个解决方案:
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 class,您可以以通用方式操作表单小部件的属性。只需添加一个字典作为 class 变量,其中包含每个小部件所需的属性和值。
这样您就可以在定义字段的同一位置添加 css classes。唯一的缺点是,您必须在某处调用“inject_css_attrs”方法,但我认为这没问题。
这是对 的回答。
如果您使用大量表单,不必每次都重写 init 的选项可能是创建您自己的表单 class:
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它会自动为你制作样式。
class ExampleForm(MyBaseForm):
# Your declared form fields here
...
通过简单地创建一个继承自 ModelForm 的 MyBaseModelForm,ModelForm 也可以完成同样的事情。
您还可以明确提及要将 class 应用到
的字段
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'})
这个很实用:
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'
这样就不用一个一个地跑了。
@christian-abbott 响应的通用版本:
class ExampleForm(forms.Form):
_HTML_CLASSES = ('form-control', 'something-else')
# Your declared form fields here
...
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for visible in self.visible_fields():
missing_classes = list(self._HTML_CLASSES)
if 'class' in visible.field.widget.attrs:
current_classes = visible.field.widget.attrs['class'].split(' ')
for current_class in current_classes:
if current_class in missing_classes:
missing_classes.remove(current_class)
else:
current_classes = []
visible.field.widget.attrs['class'] = ' '.join(current_classes + missing_classes)
我正在尝试使用 Django ModelForm 编写 Bootstrap 表单。我已经阅读了 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" class 添加到每个输入以获得 Bootstrap 输入外观(没有第三方包)。我找到了这个解决方案 Django add class to form <input ..> field。有什么方法可以在默认情况下将 class 添加到每个字段,而无需在表单 class 的 class 的每个属性中指定它?
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'}))
等等..
一种方法是创建基本表单 class 并在 __init__
方法中手动更新字段的属性。
另一种方法是使用像这样的现有库: https://github.com/dyve/django-bootstrap3
github 周围有很多这样的图书馆。环顾四周。
如果您不能使用第三方应用程序并希望以 DRY 方式向表单中的每个字段添加 class(例如“表单控件”),您可以这样做以 class __init__()
形式的方法如下:
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'
您可能还需要处理对属性中现有 classes 的检查,如果出于某种原因您将以声明方式添加 classes 和 在 __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 %}
您可以在forms.py
中添加CSS类subject = forms.CharField(label='subject', max_length=100 , widget=forms.TextInput(attrs={'class': "form-control"}))
松脆的表格是必经之路。 Bootstrap 的提示 4. 添加到@Christian Abbott 的回答,对于 forms ,bootstrap 说,使用 form-group 和 form-control 。 这就是它对我有用的方式。
我的forms.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(),则该字段不会受到影响。如果有人知道,请更新此内容。
我知道作者问过 Bootstrap 用于自己的表单,但是还有一种方法可以在 Django 表单中包含 Bootstrap class 标记以进行身份验证、密码重置等。
如果我们创建标准格式的模板:
<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 }}
现在可以用此代码替换 Bootstrap class 我们需要的是:
<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>
也许它对重新设计内置静态表单很有用。
因为我花了比我(django 新手)更多的时间来解决这个问题,所以我也会把我的结果放在这里。
为每个字段设置小部件只是为了一遍又一遍地添加一个 class 违反了重复的编程规则,并导致许多不必要的行。使用 bootstrap 表单时尤其会发生这种情况。
这是我的(工作)示例,不仅添加 bootstrap classes:
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')
templatetags/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 %}
我发现通过 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%;
}
好的,一段时间过去了,但我遇到了同样的问题。我来到这个解决方案:
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 class,您可以以通用方式操作表单小部件的属性。只需添加一个字典作为 class 变量,其中包含每个小部件所需的属性和值。 这样您就可以在定义字段的同一位置添加 css classes。唯一的缺点是,您必须在某处调用“inject_css_attrs”方法,但我认为这没问题。
这是对
如果您使用大量表单,不必每次都重写 init 的选项可能是创建您自己的表单 class:
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它会自动为你制作样式。
class ExampleForm(MyBaseForm):
# Your declared form fields here
...
通过简单地创建一个继承自 ModelForm 的 MyBaseModelForm,ModelForm 也可以完成同样的事情。
您还可以明确提及要将 class 应用到
的字段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'})
这个很实用:
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'
这样就不用一个一个地跑了。
@christian-abbott 响应的通用版本:
class ExampleForm(forms.Form):
_HTML_CLASSES = ('form-control', 'something-else')
# Your declared form fields here
...
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for visible in self.visible_fields():
missing_classes = list(self._HTML_CLASSES)
if 'class' in visible.field.widget.attrs:
current_classes = visible.field.widget.attrs['class'].split(' ')
for current_class in current_classes:
if current_class in missing_classes:
missing_classes.remove(current_class)
else:
current_classes = []
visible.field.widget.attrs['class'] = ' '.join(current_classes + missing_classes)