Wagtail - 添加 CSS class 或占位符以形成构建器字段
Wagtail - adding CSS class or placeholder to form builder fields
我正在尝试通过 Wagtail 管理表单生成器将 css 类(用于列宽)和占位符文本添加到我的 Wagtail 表单字段中。我试过使用 wagtail.contrib.forms 和 wagtailstreamforms 包都无济于事。
我知道它说 here Wagtail 表单不能替代 Django 表单。但是,如果没有这样的基本功能,它的用途就会受到限制。
下面的解决方案是一种利用 Wagtail contrib 表单构建器向 UI 添加字段的方法,CMS 用户可以在其中添加自定义 classes 和占位符字段。
实施
- 假定您有一个与 Wagtail form builder docs.
中的实现类似的工作 FormPage
- 首先,您需要向
FormField
模型添加一个新字段,在下面的示例中我称之为 field_classname
,请记住 运行 Django 迁移来更新您的数据库
- 要确保该字段显示在管理 UI 中,您需要修改
panels
,这类似于修改 Page
模型中的面板。
- 此时您应该能够打开
FormPage
的管理 UI 并查看新字段并能够将值保存到其中。
- 下一步是添加一个自定义
FormBuilder
class 来扩展通常使用的那个,然后在您的 FormPage
模型上通过 [=20= 将其设置为属性],这类似于 new fields can be added as described in the docs.
- 此
CustomFormBuilder
将使用包装函数覆盖方法 get_create_field_function
,该包装函数将 return 生成的字段(将是 Django Field instance)。
- 每个
Field
实例将被 return 编辑几乎相同,除了对 widget attrs 的更新,它本质上是一个可以添加任何内容的字典。
- 重要提示: 使用
class
属性会将 class 名称添加到字段中,但可能不会将其添加到您想要的位置,但请先尝试此操作.
- 假设您对模板渲染有更精细的控制,您需要将此 attr 传递给渲染的每个字段的模板
div
,在下面的代码中我使用了相同的 field_classname
键。
- 模板中的关键变化是提取我们
CustomFormBuilder
-> <div class="fieldWrapper {{ field.field.widget.attrs.field_classname }}" aria-required={% if field.field.required %}"true"{% else %}"false"{% endif %}>
设置的小部件属性自定义值
models.py
from django.db import models
from modelcluster.fields import ParentalKey
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.contrib.forms.forms import FormBuilder
from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField
# ... other imports
class FormField(AbstractFormField):
page = ParentalKey("FormPage", related_name="form_fields", on_delete=models.CASCADE)
# add custom fields to FormField model
field_classname = models.CharField("Field classes", max_length=254, blank=True)
placeholder = models.CharField("Placeholder", max_length=254, blank=True)
# revise panels so that the field can be edited in the admin UI
panels = AbstractFormField.panels + [
FieldPanel("field_classname"),
FieldPanel("placeholder"),
]
class CustomFormBuilder(FormBuilder):
def get_create_field_function(self, type):
"""
Override the method to prepare a wrapped function that will call the original
function (which returns a field) and update the widget's attrs with a custom
value that can be used within the template when rendering each field.
"""
create_field_function = super().get_create_field_function(type)
def wrapped_create_field_function(field, options):
created_field = create_field_function(field, options)
created_field.widget.attrs.update(
# {"class": field.field_classname} # Important: using the class may be sufficient, depending on how your form is being rendered, try this first.
{"field_classname": field.field_classname} # this is a non-standard attribute and will require custom template rendering of your form to work
{"placeholder": field.placeholder},
)
return created_field
return wrapped_create_field_function
class FormPage(AbstractEmailForm):
form_builder = CustomFormBuilder # use custom form builder to override behaviour
# ... other form page fields, panels etc
templates/form_page.html
{% comment %}
You could render your form using a Django rendering shortcut such as `{{ form.as_p }}` but that will tend towards unsemantic code, and make it difficult to style. You can read more on Django form at:
https://docs.djangoproject.com/en/1.10/topics/forms/#form-rendering-options
{% endcomment %}
<form action="{% pageurl page %}" method="POST" role="form">
{% csrf_token %}
{% if form.subject.errors %}
<ol role="alertdialog">
{% for error in form.subject.errors %}
<li role="alert"><strong>{{ error|escape }}</strong></li>
{% endfor %}
</ol>
{% endif %}
{% for field in form %}
<div class="fieldWrapper {{ field.field.widget.attrs.field_classname }}" aria-required={% if field.field.required %}"true"{% else %}"false"{% endif %}>
{{ field.label_tag }}{% if field.field.required %}<span class="required">*</span>{% endif %}
{{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
<input type="submit">
</form>
我正在尝试通过 Wagtail 管理表单生成器将 css 类(用于列宽)和占位符文本添加到我的 Wagtail 表单字段中。我试过使用 wagtail.contrib.forms 和 wagtailstreamforms 包都无济于事。
我知道它说 here Wagtail 表单不能替代 Django 表单。但是,如果没有这样的基本功能,它的用途就会受到限制。
下面的解决方案是一种利用 Wagtail contrib 表单构建器向 UI 添加字段的方法,CMS 用户可以在其中添加自定义 classes 和占位符字段。
实施
- 假定您有一个与 Wagtail form builder docs. 中的实现类似的工作
- 首先,您需要向
FormField
模型添加一个新字段,在下面的示例中我称之为field_classname
,请记住 运行 Django 迁移来更新您的数据库 - 要确保该字段显示在管理 UI 中,您需要修改
panels
,这类似于修改Page
模型中的面板。 - 此时您应该能够打开
FormPage
的管理 UI 并查看新字段并能够将值保存到其中。 - 下一步是添加一个自定义
FormBuilder
class 来扩展通常使用的那个,然后在您的FormPage
模型上通过 [=20= 将其设置为属性],这类似于 new fields can be added as described in the docs. - 此
CustomFormBuilder
将使用包装函数覆盖方法get_create_field_function
,该包装函数将 return 生成的字段(将是 Django Field instance)。 - 每个
Field
实例将被 return 编辑几乎相同,除了对 widget attrs 的更新,它本质上是一个可以添加任何内容的字典。 - 重要提示: 使用
class
属性会将 class 名称添加到字段中,但可能不会将其添加到您想要的位置,但请先尝试此操作. - 假设您对模板渲染有更精细的控制,您需要将此 attr 传递给渲染的每个字段的模板
div
,在下面的代码中我使用了相同的field_classname
键。 - 模板中的关键变化是提取我们
CustomFormBuilder
-><div class="fieldWrapper {{ field.field.widget.attrs.field_classname }}" aria-required={% if field.field.required %}"true"{% else %}"false"{% endif %}>
设置的小部件属性自定义值
FormPage
models.py
from django.db import models
from modelcluster.fields import ParentalKey
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.contrib.forms.forms import FormBuilder
from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField
# ... other imports
class FormField(AbstractFormField):
page = ParentalKey("FormPage", related_name="form_fields", on_delete=models.CASCADE)
# add custom fields to FormField model
field_classname = models.CharField("Field classes", max_length=254, blank=True)
placeholder = models.CharField("Placeholder", max_length=254, blank=True)
# revise panels so that the field can be edited in the admin UI
panels = AbstractFormField.panels + [
FieldPanel("field_classname"),
FieldPanel("placeholder"),
]
class CustomFormBuilder(FormBuilder):
def get_create_field_function(self, type):
"""
Override the method to prepare a wrapped function that will call the original
function (which returns a field) and update the widget's attrs with a custom
value that can be used within the template when rendering each field.
"""
create_field_function = super().get_create_field_function(type)
def wrapped_create_field_function(field, options):
created_field = create_field_function(field, options)
created_field.widget.attrs.update(
# {"class": field.field_classname} # Important: using the class may be sufficient, depending on how your form is being rendered, try this first.
{"field_classname": field.field_classname} # this is a non-standard attribute and will require custom template rendering of your form to work
{"placeholder": field.placeholder},
)
return created_field
return wrapped_create_field_function
class FormPage(AbstractEmailForm):
form_builder = CustomFormBuilder # use custom form builder to override behaviour
# ... other form page fields, panels etc
templates/form_page.html
{% comment %}
You could render your form using a Django rendering shortcut such as `{{ form.as_p }}` but that will tend towards unsemantic code, and make it difficult to style. You can read more on Django form at:
https://docs.djangoproject.com/en/1.10/topics/forms/#form-rendering-options
{% endcomment %}
<form action="{% pageurl page %}" method="POST" role="form">
{% csrf_token %}
{% if form.subject.errors %}
<ol role="alertdialog">
{% for error in form.subject.errors %}
<li role="alert"><strong>{{ error|escape }}</strong></li>
{% endfor %}
</ol>
{% endif %}
{% for field in form %}
<div class="fieldWrapper {{ field.field.widget.attrs.field_classname }}" aria-required={% if field.field.required %}"true"{% else %}"false"{% endif %}>
{{ field.label_tag }}{% if field.field.required %}<span class="required">*</span>{% endif %}
{{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
<input type="submit">
</form>