使用 Crispy Forms 渲染 django 的 Multiwidget 和 MutliValueField Textarea

Render django's Multiwidget & MutliValueField Textarea using Crispy Forms

我有一个 TextField 用于存储大量文本,这些文本可以在逻辑上分为 10 个部分。我认为创建 10 个单独的 Textarea 是有意义的,每个用于一个逻辑部分。因此,我像这样子class编辑了MultiWidgetMultiValueField

class MultiWidget(forms.widgets.MultiWidget):
    template_name = "custom_content_widget.html"
    attrs = {"class": "textarea form-control"}

    def __init__(self, attrs=None):
        widgets = [Textarea()] * 10
        super(MultiWidget, self).__init__(widgets, attrs)

    def decompress(self, value):
        if value:
            return value
        return ["", "", "", "", "", "", "", "", "", ""]


class ContentField(MultiValueField):
    widget = MultiWidget

    def __init__(self, *args, **kwargs):
        # Define one message for all fields.
        error_messages = {
            'required': 'This field is required.',
        }
        # Or define a different message for each field.
        fields = [CharField()] * 10

        super(ContentField, self).__init__(
            error_messages=error_messages, fields=fields, require_all_fields=True, *args, **kwargs)

        # self.helper = FormHelper()
        # self.helper.layout = Layout(
        #
        # )

    def compress(self, data_list):
        return " ".join(data_list)

custom_content_widget.html 只是

{% for subwidget in widget.subwidgets %}
    {% with widget=subwidget %}
        {% include widget.template_name %}
    {% endwith %}
{% endfor %}

我想在其中使用此 multiwidget 的简单模型和形式

class Opinion(models.Model):
    content = models.TextField()

class OpinionForm(forms.ModelForm):
    content = ContentField()

    class Meta:
        model = Opinion
        fields = ('__all__')

问题是,当我在表单的 HMTL 中使用 content 作为 {{ form.content | as_crispy_field }} 时,它呈现得非常丑陋 我希望所有 Textarea 都呈现在另一个下方。这里的主要问题是 textarea 呈现为

<textarea name="content_0" cols="40" rows="10" class="textarea" required id="id_content_0">
</textarea>

而“正常”TextField 呈现为

<textarea name="content" cols="40" rows="10" class="textarea form-control" required id="id_content">
</textarea>

而且我不知道如何强制小部件的 class 为 textarea form-control 而不是 textarea。最初,我发现 this question and also this blog 但他们所做的只是将小部件正确地分组到行和列中。我在这里遗漏了什么吗?

关键是将带有 "class": "textarea form-control" 的属性字典传递给 MultiWidget 构造函数,如下所示

class ContentField(MultiValueField):
    widget = MultiWidget({"class": "textarea form-control"})

    def __init__(self, *args, **kwargs):
        # Define one message for all fields.
        error_messages = {
            'required': 'This field is required.',
        }
        # Or define a different message for each field.
        fields = [CharField()] * 10

        super(ContentField, self).__init__(
            error_messages=error_messages, fields=fields, require_all_fields=True, *args, **kwargs)

    def compress(self, data_list):
        return " ".join(data_list)