Disclaimer: I would still recommend using django-widget-tweaks in most cases. Nonetheless, I want to include the below answer separately, because it can still be useful and isn't documented anywhere else that I can find.
I'm not sure about earlier versions, but in Django 5.0 there is a slightly easier way of handling this.
If you're using a ModelForm, then you can modify the BoundField objects to specify a css_classes attribute. I presume the same is true for standard (non-model) form classes, but I have not confirmed it.
class ModelForm(forms.ModelForm):
# (optional - see notes below)
# template_name = "myapp/forms/div.html"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self["name"].css_classes = "special"
For more general approach, you can iterate over the form class's form field objects to perform some logic to dynamically assign a class based on field type, widget, or whatever suits your needs.
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field_name, field in self.fields.items():
# where field_name is "name"
# where field is <django.forms.fields.CharField object...>
self[field_name] = ...
An important note here is the difference between the form field objects (like forms.CharField) and the BoundField objects. The former is the field definition for the form class, but the latter is what's used to render the template.
self.name # <-- form field object (does not have css_classes attribute)
self["name"] # <-- BoundField object (has css_classes attribute)
Optionally, you can specify the template_name class attribute for your form class to point to a relative path from your app's templates/ directory to use instead of Django's standard template.
Note: There are other form class attributes (like form_template_name or template_name_div, among others) which can probably be used to to control this more granularly. This is just what I found to work for me.
References:
- (Django Docs) The Forms API: