How to modify attributes of the Wagtail form input fields?

pagechooser wagtail
wagtail fields
wagtail image field
wagtail widgets
documentchooserpanel
django form widgets example
wagtail radio button
django widgets

I'm looking at wagtail hello-world form example:

class FormField(AbstractFormField):
    page = ParentalKey('FormPage', related_name='form_fields')


class FormPage(AbstractEmailForm):
    landing_page_template = 'blog/form_page.html'
    intro = RichTextField(blank=True)
    thank_you_text = RichTextField(blank=True)

    content_panels = AbstractEmailForm.content_panels + [
        FormSubmissionsPanel(),
        FieldPanel('intro', classname="full"),
        InlinePanel('form_fields', label="Form fields"),
        FieldPanel('thank_you_text', classname="full"),
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('from_address', classname="col6"),
                FieldPanel('to_address', classname="col6"),
            ]),
            FieldPanel('subject'),
        ], "Email"),
    ]

If given the template like this:

{% for field in form %}
  {{ field }} 
{% endfor %}

It will render HTML similar to the following:

<input type="text" name="label1" value="Default Label1" required maxlength="255" id="id_label1" />
<input type="url" name="label2" value="http://Label2.net" id="id_label2" />
<textarea name="label3" required rows="10" id="id_label3" cols="40">

How can I modify/add extra attributes to the rendered input fields?

For instance:

  • How to add class="form-control"?
  • How to change rows="5" in the textarea input?

Is there any other convenient way apart from javascript madness?

I assume you are wanting to change how FormPage's are rendered (ie. pages that extend AbstractEmailForm or AbstractForm).

There are some ways to do this from within your form_page.html template (see links at bottom of answer), however, I think your best bet is to add these attributes before they get to the template.

This way you can keep your template really simple, even just using a copy and paste from the docs example.

{% load wagtailcore_tags %}
<html>
    <head>
        <title>{{ page.title }}</title>
    </head>
    <body>
        <h1>{{ page.title }}</h1>
        {{ page.intro|richtext }}
        <form action="{% pageurl page %}" method="POST">
            {% csrf_token %}
            {{ form.as_p }}
            <input type="submit">
        </form>
    </body>
</html>

Then, in my_app/models.py you want to override the get_form method on your FormPage. You can see what the get_form method does in the Wagtail form/models.py code.

You need to ensure that you are getting the generated form object via super. Then iterate through the fields - updating the widget on each field. Widgets are used to render the input field (or Textarea) and by default the Textarea widget contains 10 rows and 40 cols.

You an also update the attrs dictionary on any widget with the class key to add custom classes to the rendered input. This way we can add form-control to every single input.

See example:

from django.forms import widgets  # used to find TextArea widget
# other imports.... AbstractEmailForm etc

class FormPage(AbstractEmailForm):
    # body, thank_you_text, etc

    def get_form(self, *args, **kwargs):
        form = super().get_form(*args, **kwargs)
        # form = super(AbstractEmailForm, self).get_form(*args, **kwargs)  # use this syntax for Python 2.x
        # iterate through the fields in the generated form
        for name, field in form.fields.items():
            # here we want to adjust the widgets on each field
            # if the field is a TextArea - adjust the rows
            if isinstance(field.widget, widgets.Textarea):
                field.widget.attrs.update({'rows': '5'})
            # for all fields, get any existing CSS classes and add 'form-control'
            # ensure the 'class' attribute is a string of classes with spaces
            css_classes = field.widget.attrs.get('class', '').split()
            css_classes.append('form-control')
            field.widget.attrs.update({'class': ' '.join(css_classes)})
        return form

This will render all textarea inputs with rows=5 in the html attributes and also add form-control to the class attributes of all inputs (even hidden inputs if used). Remember that this will change every single FormPage in use.

For more complex rendering of forms within the template, this article is really good but it gets pretty full on: https://simpleisbetterthancomplex.com/article/2017/08/19/how-to-render-django-form-manually.html#accessing-the-form-fields-individually

Also, for basic Bootstrap 3 (or 4) rendering of forms, Crispy Forms may save you a lot of hassle.

How to modify attributes of the Wagtail form input fields?, Amongst other things, this allows you to customise the HTML attributes that appear on the field: from django import forms class FormPage( AbstractEmailForm):� Override Field Widget Attributes in a Django Form or How To Add Placeholder Attribute to Django Form Input 18 Jan · by Tim Kamanin · 1 min read Let's say we have a contact form and we want to add placeholders to this form inputs so our rendered form has these cool input labels.

You can create dynamic templates: Create a loop through form django doc

{% for field in form.visible_fields %}
   <div>
      <div class="form-group mb-10">
         {{ field.errors }}
         {{ field.label_tag }}
         <input type="{{ field.field.widget.input_type }}" class="form-control"
                name="{{ field.name }}" id="{{ field.id_for_label }}"
                {% if field.field.required %}required="required"{% endif %}>
         {% comment %}you might want form field for textarea or select etc. just make conditions for those {% endcomment %}
      </div>
  </div>
 {% endfor %}

Customising the editing interface — Wagtail 2.2 documentation, However, we may wish to restrict a rich text field to a more limited set of form class can be configured by setting the base_form_class attribute on any model. The formtarget Attribute. The input formtarget a attribute specifies a name or a keyword that indicates where to display the response that is received after submitting the form. Note: This attribute overrides the target attribute of the <form> element. The formtarget attribute works with the following input types: submit and image.

You can pass a widget argument to FieldPanel, to define the Django widget object to use with that field. Amongst other things, this allows you to customise the HTML attributes that appear on the field:

from django import forms

class FormPage(AbstractEmailForm):
    content_panels = [
        # ...
        FieldPanel('subject', widget=forms.TextInput(attrs={'class': 'form-control'})),
    ]

Form builder customisation — Wagtail 2.10 documentation, If you want to change related_name for form fields (by default AbstractForm and Override the field_type field in your FormField model with choices attribute� Problem: I have an modal window in my cred form. I would get two value's back to my cred form. When i click on the link in de modal form i would like that the input field naam is filled in and i would get the number from the href in an other field.

This is an extension of the answer provided by w411-3 ...

If you want to keep all this within the template, and have a multi-line textarea you can take advantage of the {{ field.field.widget.input_type }} which supplies a True for all but the textarea field.

<form action="{% pageurl page %}" method="POST">
    {% csrf_token %}

    {% for field in form.visible_fields %}
    <div class="form-group mb-10">
        {{ field.errors }}
        <label for="{{ field.id_for_label }}">{{ field.label }}</label>

        {% if field.field.widget.input_type %}

        <input type="{% if field.field.widget.input_type == 'number' %}
                          tel
                     {% else %}
                          {{ field.field.widget.input_type }}
                     {% endif %}" 
            class="form-control" 
            name="{{ field.name }}"
            id="{{ field.id_for_label }}"
            placeholder="{{ field.help_text }}"
            {% if field.field.required %}required="required"{% endif %}>

        {% else %}

        <textarea type="{{ field.field.widget.input_type }}" 
            class="form-control" 
            name="{{ field.name }}"
            id="{{ field.id_for_label }}"
            placeholder="{{ field.help_text }}"
            {% if field.field.required %}required="required"{% endif %}
            ></textarea>

        {% endif %}
    </div>
    {% endfor %}

    <input type="submit">
</form>

You could replace the html label definition with : {{ field.label_tag }}, however this method allows you to easily add extra css classes if needed.

The if statement within the input type removes the arrow / spinners that are automatically added to number fields. For some reason Wagtail does not provide the telephone data type in forms which would make this unnecessary.

Note: If you want custom elements for each form type, look at the Wagtail Documentation on Rendering Templates for StreamFields. Instead of if block.block_type == 'heading' you could use something similar to field.field.widget.input_type == 'number' and perhaps create a more elegant solution.

This source is now available as a Wagtail app on GitHub.

Displaying fields with the Editing API — Wagtail 0.8.6 documentation, There is also an Edit Handler API for creating your own Wagtail editor components. Without a panel definition, a default form field (without label) will be used to By default only a limited set of HTML elements and attributes are whitelisted� Add a method that will return a created Django form field for the new field type. Its name must be in the format: create_<field_type_key>_field, e.g. create_slug_field; Override the form_builder attribute in your form page model to use your new form builder class. Example:

Form builder customisation — Wagtail 2.0 documentation, If you want to change related_name for form fields (by default AbstractForm and Override the field_type field in your FormField model with choices attribute� Open a form in the form editor and below the Field Explorer click New Field to create a new field. For any field already added to the form you can double-click the field to display the Field Properties. On the Details tab, click Edit. Another way to go to the form editor is to use the Form command on the command bar for any entity record.

Displaying fields with the Editing API — Wagtail 0.8.10 documentation, There is also an Edit Handler API for creating your own Wagtail editor components. Without a panel definition, a default form field (without label) will be used to By default only a limited set of HTML elements and attributes are whitelisted� For further guidance, refer to the source code of Wagtail’s built-in block classes. For block types that simply wrap an existing Django form field, Wagtail provides an abstract class wagtail.core.blocks.FieldBlock as a helper. Subclasses just need to set a field property that returns the form field object:

Customising CreateView, EditView and DeleteView — Wagtail 2.8.1 , NOTE: modeladmin only provides 'create', 'edit' and 'delete' functionality for the EditView, simply define an edit_handler or panels attribute on your model class. instances, this value will be passed to the edit form, so that any named fields� I have generated a simple contact form with four fields using Wagtail Form Builder. The challenge I'm having, is that I'm unable to reference the form fields with css classes individually, since I'm not sure where I should get the field id and label name from. Here is the code that renders the form:

Comments
  • Brilliant! 2 years later and this helped boost my ego to a coworker. Thank you. I modified this to add a placeholder text to each form field. I'm using the help text in my case because we do not use it in this particular project. field.widget.attrs.update({'placeholder': field.help_text})
  • Awesome to hear.