I have a TextField
intended to store a large amount of text that can be logically split into 10 parts. I thought that it would make sense to create 10 separate Textarea
s, each for one logical part. Thus, I subclassed MultiWidget
and MultiValueField
like so:
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)
with the custom_content_widget.html
being just
{% for subwidget in widget.subwidgets %}
{% with widget=subwidget %}
{% include widget.template_name %}
{% endwith %}
{% endfor %}
Simple model and form in which I'd like to use this multiwidget
class Opinion(models.Model):
content = models.TextField()
class OpinionForm(forms.ModelForm):
content = ContentField()
class Meta:
model = Opinion
fields = ('__all__')
The problem is that when I use content
in my form's HMTL as {{ form.content | as_crispy_field }}
it renders pretty ugly
and I'd like all of the Textarea
s to be rendered one under the other. The main issue here is that textarea
is rendered as
<textarea name="content_0" cols="40" rows="10" class="textarea" required id="id_content_0">
</textarea>
while "normal" TextField
is rendered as
<textarea name="content" cols="40" rows="10" class="textarea form-control" required id="id_content">
</textarea>
and I have no clue how could I force the class of the widget to be textarea form-control
instead of textarea
. Initially, I found this question and also this blog but all they do is just to properly group widgets into rows and columns. Is there anything I am missing here?
The key was to pass attributes dict with "class": "textarea form-control"
to the MultiWidget
constructor as follows
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)