1

I am trying to customise a django form for use with bootstrap 4, custom html layout & per field class or id names on the FormModel defintion

I have the following html

{% for hidden_field in form.hidden_fields %}
    {{ hidden_field }}
{% endfor %}

{% if form.non_field_errors %}
    <div class="alert alert-danger" role="alert">
        {% for error in form.non_field_errors %}
            {{ error }}
        {% endfor %}
    </div>
{% endif %}

{% for field in form.visible_fields %}
    <div class="form-group">
        {{ field.label_tag }}
        {% if form.is_bound %}
            {% if field.errors %}
                {% render_field field class="form-control is-invalid" %}
                {% for error in field.errors %}
                    <div class="invalid-feedback">
                        {{ error }}
                    </div>
                {% endfor %}
            {% else %}
                {% render_field field class="form-control is-valid" %}
            {% endif %}
        {% else %}
            {% render_field field class="form-control" %}
        {% endif %}

        {% if field.help_text %}
            <small class="form-text text-muted">{{ field.help_text }}</small>
        {% endif %}
    </div>
{% endfor %}

And the following form defintion:

class DocumentForm(forms.ModelForm):
    field1 = PartLookupField(required=True, widget=forms.TextInput(attrs={'class': 'field1-choice-ajax'}))
    field2 = forms.CharField(required=True, widget=forms.TextInput(attrs={'id': 'field2-field'}))
    form_lines = forms.CharField(widget=forms.HiddenInput())

    class Meta:
        model = Document
        fields = ("field1", "field2", "form_lines")

So essentially, I need to get the per field definition of id or class, from the widget on the model, and combine that with the template defined form-control or is-valid/invalid classes in the template.

I've tried going down this route

How to concatenate strings in django templates?

But it just seems like it's going to end up in a huge mess.

Essentially, how can I combine template defined attributes and per field defined attributes? I need to end up with class="form-control field1-choice-ajax" for the field specified in the model (and the correct extra class names for the valid/invalid states).

Previously I was using the bootstrap4 form library, but I need complete control now:

{% csrf_token %}
{% bootstrap_form form %}
Chris Barry
  • 4,564
  • 7
  • 54
  • 89

2 Answers2

2

I've created my own template filter in order to add class attributes to existing form fields:

@register.filter
def add_class(field, css):
    """Add a class to a field in a template.

    Example:
        > {{ form.my_field|add_class:"required" }}
        <input id="my_field_id" name="my_field" class="required" type="text">

    Args:
        field: this should be a form field, of type ``BoundField``
        css: this should be a string with one or more class names separated by spaces
    """
    class_old = field.field.widget.attrs.get('class', None)
    class_new = class_old + ' ' + css if class_old else css
    return field.as_widget(attrs={'class': class_new})

So now I can do this in a template:

{{ field|add_class:"is-valid" }}
dirkgroten
  • 20,112
  • 2
  • 29
  • 42
0

Use Widget Tweaks.

It allows you to do something like:

{{ field|add_class:'form-control' }}

Alexander Suraphel
  • 10,103
  • 10
  • 55
  • 90