Styling form.as_div (or form.as_p)

Widget classes allow for CSS styling via the attrs attribute. That works pretty well for form fields. Is there a way achieve the same effect with form.as_div()?

form.as_div() wraps div tags around the widgets. I would like to do something like:

<div class="wgt-div">
    <label for="number-input">Add number</label>
    <input id="number-input" type="number"/>
</div>

note the CSS class “wgt-div” attached to the div and not the widgets. Is this viable? How?

here is what I have, so far:

from django.utils.safestring import SafeString

class BaseTestFormSetforms(forms.BaseModelFormSet):
    """Basis for the set of forms used for Test"""

    def as_div(self):
        """Add CSS Styling to divs"""

        return SafeString(super().as_div().replace("<div>", "<div class='widget-group'>"))

A hack! One snag … some of the div lines have no newline character (“\n”), hence the source html is not completely neat. I wonder what the native as_div() function looks like

The as_div function is really simple - it renders a template. (see django.forms.utils.RenderableFormMixin)

Take a look at the div.html file in django.forms.templates.django.forms. You’ll see that the div wrapper uses the attribute css_classes to define what to use in the class attribute of the div.

Also see The Forms API | Django documentation | Django

1 Like

I looked at it before implementing the subclass above. I wasn’t sure how the css_classes worked. It appears to be addressing fields contained in the wrapper and not the div wrapper itself

Did you look at the referenced div.html file? The template makes it clear how it’s being used.

I get it now.

I needed to set the required_css_class attribute in the form (See this). Otherwise, the following line in div.html remained a puzzle:

this line:
<div{% with classes=field.css_classes %}{% if classes %} class="{{ classes }}"{% endif %}{% endwith %}>

something had to set values before field.css_classes function returns the value. Automatically generated forms don’t have that advantage. I was using modelformset_factory without setting a custom form. Hence, django was generating those forms from the models using the default values for . required_css_class (i.e not set)

Invariably, one must define a form class to take advantage of custom as_div() styling. I see no way of setting required_css_class after instantiating the form

If you really wanted to address this in a different way, you could iterate over the fields over all the forms in the formset after creating it, and set that attribute on the individual fields.

While this is close, it does not solve my problem especially for formsets with “can_delete” fields.
The delete field has a separate div wrapping. That presents more styling challenges for me. I want all the fields in a member form inside a div. Looks like I have to iterate on the template. AArghhh!

Hey! I solved using the next lines:

from django.utils.safestring import SafeString

class MyForm(forms.Form):
    #class to every widget
    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        for visible in self.visible_fields():
            visible.field.widget.attrs['class'] = 'form-control'
    #class on every div when I use {{ form.as_div }} on html template
    def as_div(self):
        return SafeString(super().as_div().replace("<div>", "<div class='form-group'>"))
    
    ...
1 Like