Override default CSS classes using WTForms' render_kw() - flask-wtforms

In a Flask app with the Bootstrap-Flask extension, how would one override the default style using WTForms' render_kw class parameter when the form's HTML is automatically rendered using render_form()? [*]
I have this simple Form defined that provides a button to confirm the deletion of a database record, and I'd like that button to be red instead of the standard grey color:
class DeleteMappingForm(FlaskForm):
submit = SubmitField(_l('Delete mapping'))
The auto-generated HTML (focusing just on the <input> element) is:
<input class="btn btn-secondary btn-md" id="submit" name="submit" type="submit" value="Delete mapping">
I'd like to use the btn-alert instead of the default btn-secondary Bootstrap class to display the button in red instead of grey.
The WTForms way of defining "keywords that will be given to the widget at render time" is to pass those as a dict in render_kw. That works fine to add new keywords, but it doesn't replace existing, which I tried doing like this:
class DeleteMappingForm(FlaskForm):
submit = SubmitField(_l('Delete mapping'), render_kw={'class':'btn btn-alert btn-md'})
That ends up adding the CSS classes I defined to the list of pre-defined CSS classes, ending up with this HTML:
<input class="btn btn-secondary btn-md btn btn-alert btn-md" id="submit" name="submit" type="submit" value="Delete mapping">
Is there a way to have the values passed via render_kw to override the default classes, and have just class="btn btn-alert btn-md"?
Extra information
Looking at how render_kw is handled by WTForms, it seems the values should be overriden (dict gets concatenated, which overrides the same keys, eg.:
>>> a={"class":"abc", "other":"aaa"}
>>> a
{'class': 'abc', 'other': 'aaa'}
>>> b={"class":"def", "other":"aaa"}
>>> b
{'class': 'def', 'other': 'aaa'}
>>> dict(a, **b)
{'class': 'def', 'other': 'aaa'}
I couldn't also identify a source of this in Bootstrap-Flask's handling of render_form()...
[*] I'm using the Bootstrap-Flask extension instead of the original Flask-Bootstrap because the latter unfortunately doesn't support Bootstrap 4+. render_form() is the Bootstrap-Flask equivalent of Flask-Bootstrap's quick_form().

Turns out the solution is to be found on the side of Bootstrap-Flask, and that it already provides a button_style parameter to the render_form macro, allowing to pass a Bootstrap class directly.
The following template gets me that red button I was looking for ;) As easy as that...
{% extends "base.html" %}
{% import 'bootstrap/form.html' as wtf %}
{% block app_content %}
<h1>{{ title }}</h1>
<div class="row">
<div class="col-lg-8">
{{ wtf.render_form(form, button_style="danger") }}
</div>
</div>
{% endblock %}
Well, that is kind of embarrassing - but I'm happy nonetheless to have found the solution, too bad for the time spent writing up this question, but as this might help others with the same challenge I'm adding my answer here.

Related

How to style mixed django form better

I have theese two forms:
class InitialForm(Form):
trn = IntegerField(widget=NumberInput, required = False)
klient = ChoiceField(choices=KLIENTS, required = False)
class SecondForm(Form):
faktura = CharField(max_length = 200, required=False)
In the form I'm mixing django forms and pure html select (because I needed to pull out some data from a database)
<form method="POST" novalidate>
{% csrf_token %}
<label for="{{ form.klient.id_for_label }}">Klient: </label>
{{ form.klient }}
<br><br>
<label for="programs">Program: </label>
<select id="programselect" name="programs">
{% for option in options %}
<option value="{{ option.0}}">{{ option.1 }}</option>
{% endfor %}
</select>
<br><br>
<label for="{{ form.trn.id_for_label }}">Trn: </label>
{{ form.trn }}
<br><br>
<label for="{{ form2.faktura.id_for_label }}">Faktura: </label>
{{ form2.faktura }}
<br>
<button type="submit" class="btn btn-secondary">Search</button>
</form>
So far, I tried it with &nbsp& to have the inputs above each other.
How would you style it better with CSS?
Assuming you're using the crispy template pack for styling, you could create a model form class and give the programs field initial "options" from the database, using a query from whatever model you're storing the "options" on. Here's an example, using some assumptions about your database schema:
class ProgramForm(forms.ModelForm):
class Meta:
fields = ['program']
model = MyRecord
def __init__(self, *args, **kwargs):
super(ProgramForm, self).__init__(*args, **kwargs)
self.fields['program'].queryset = Option.objects.filter(some_field=True).order_by("option_name")
Using this kind of solution, you're using your backend model form class to handle passing the "option" data, and doing all your styling in the html with crispy. Note that if you're using two separate forms, pass them to your template with different names, like:
{{form.klient|as_crispy_field}}
{{program_form.program|as_crispy_field}}
Additionally, you can use Django's widgets to add functionality to your options selector, for example if you wanted checkboxes instead of a dropdown select (ForeignKey vs M2M, let's say).
If you can do none of that and must use this HTML structure and backend approach, you can still target the form with CSS, for example something like:
<style>
#this-label span {
margin-right: 100px;
}
</style>
<label for="{{ form.trn.id_for_label }}" id="this-label"> <span>Trn:</span> </label>
...will add space to the right of "Trn:". This is just one way of doing that, but you could add a class to the span element and target multiple such tags.

Change label color of Django user form

I added a user module to my project and I used the default Django user model for that.
I've to change the styling to suit my other pages. The background color is black. The problem is default labels and error messages (eg: "username", "password", "Enter the same password as before for verification") generated by Django is also black and not visible now. How to change the label colors so that they are visible again? I'm a newbie to both development & StackOverflow, apologies if I have used incorrect terms or tags in my question. Thanks in advance.screenshot of the login form (highlighted is my problem
This is my forms.py (users app)
from django.contrib.auth.forms import UserCreationForm
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
fields = UserCreationForm.Meta.fields + ("email",)
this is from my login.html
<h2>Login</h2>
<div>
<form method="post" class="loginfrm">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Login" class="loginbtn">
</form>
</div>
I've figured it out, thanks to Mubashar Javed's comment.
<form method="post" class="loginfrm">
I had to apply the style to this class, any style I apply to this class, only affects the labels. Exactly what I wanted.
<style>
.loginfrm {
color: white;
}
</style>
you can see that my problem is now fixed.

Drupal 8 - reach node/content needed variables in view

New user of D8 : my problem is to access to fields in a view or even in general with Drupal 8.
As we could do with ACF in Wordpress.
a {{ kint() }} crash my chrome but works with Firefox to explore the content var.
Unfortunately I do not managed to find and use fields' variables in my view.
I create a new view, which actually display the last three articles. These are well displayed in ugly list but I want to extract fields to put them in a custom html integration.
I create and use a new template for the view :
x node--view--liste-des-actualites--page-2.html.twig
In a custom parent :
x node--page-accueil.html.twig
But when I try to kint() content in my node--view--liste-des-actualites--page-2.html.twig, I have the custom field of the page (Page accueil) and can't find the article's one.
I managed to do it in my custom page but not in this view.
{%
set classes = [
'node',
'node--type-' ~ node.bundle|clean_class,
node.isPromoted() ? 'node--promoted',
node.isSticky() ? 'node--sticky',
not node.isPublished() ? 'node--unpublished',
view_mode ? 'node--view-mode-' ~ view_mode|clean_class,
'clearfix',
]
%}
{{ attach_library('classy/node') }}
<article{{ attributes.addClass(classes) }}>
<div{{ content_attributes.addClass('node__content', 'clearfix') }}>
{{ content }}
<div class="col-lg-4 col-md-4 col-sm-4 col-xs-4">
<a href="{{ LINK_VAR }}" class="bloc-type">
<div class="categ categ_projet">{{ CATEGORY_VAR }}</div>
<div class="img"> <img src="{{ IMAGESRC_VAR }}" alt=""> </div>
<span class="wrapper">
<p class="date">{{ DATE_VAR }}</p>
<h3>{{ TITLE_VAR }}</h3>
</span>
</a>
</div>
</div>
</article>
EDIT
I managed to guess some fields but this is definitely not a good way to find variables..
{{ node.label }} + {{ content.field_tags }} (But I do not want a rendered one, I just want the text/value)
if you use kint(); to debug large arrays can crash your browser.
I would suggest to use the devel module https://www.drupal.org/project/devel. With devel you can debug your arrays inside of the Drupal8 UI for each content type, block or view.
In my case i use the UI of devel (additional tab on each content). in the module settings, you can chose how devel debugs, the error handling and the output.
As the OP commented it is possible to use a preprocess to display the array on your site:
function <themename>_preprocess_page(&$variables) {
dpm($variables);
}

Add a css class to a field in wtform

I'm generating a dynamic form using wtforms (and flask). I'd like to add some custom css classes to the fields I'm generating, but so far I've been unable to do so. Using the answer I found here, I've attempted to use a custom widget to add this functionality. It is implemented in almost the exact same way as the answer on that question:
class ClassedWidgetMixin(object):
"""Adds the field's name as a class.
(when subclassed with any WTForms Field type).
"""
def __init__(self, *args, **kwargs):
print 'got to classed widget'
super(ClassedWidgetMixin, self).__init__(*args, **kwargs)
def __call__(self, field, **kwargs):
print 'got to call'
c = kwargs.pop('class', '') or kwargs.pop('class_', '')
# kwargs['class'] = u'%s %s' % (field.name, c)
kwargs['class'] = u'%s %s' % ('testclass', c)
return super(ClassedWidgetMixin, self).__call__(field, **kwargs)
class ClassedTextField(TextField, ClassedWidgetMixin):
print 'got to classed text field'
In the View, I do this to create the field (ClassedTextField is imported from forms, and f is an instance of the base form):
f.test_field = forms.ClassedTextField('Test Name')
The rest of the form is created correctly, but this jinja:
{{f.test_field}}
produces this output (no class):
<input id="test_field" name="test_field" type="text" value="">
Any tips would be great, thanks.
You actually don't need to go to the widget level to attach an HTML class attribute to the rendering of the field. You can simply specify it using the class_ parameter in the jinja template.
e.g.
{{ form.email(class_="form-control") }}
will result in the following HTML::
<input class="form-control" id="email" name="email" type="text" value="">
to do this dynamically, say, using the name of the form as the value of the HTML class attribute, you can do the following:
Jinja:
{{ form.email(class_="form-style-"+form.email.name) }}
Output:
<input class="form-style-email" id="email" name="email" type="text" value="">
For more information about injecting HTML attributes, check out the official documentation.
If you would like to programatically include the css class (or indeed, any other attributes) to the form field, then you can use the render_kw argument.
eg:
r_field = RadioField(
'Label',
choices=[(1,'Enabled'),(0,'Disabled')],
render_kw={'class':'myclass','style':'font-size:150%'}
)
will render as:
<ul class="myclass" id="r_field" style="font-size:150%">
<li><input id="r_field-0" name="r_field" type="radio" value="1"> <label for="r_field-0">Enabled</label></li>
<li><input id="r_field-1" name="r_field" type="radio" value="0"> <label for="r_field-1">Disabled</label></li>
</ul>
In WTForms 2.1 I using extra_classes, like the line bellow:
1. The first way
{{ f.render_form_field(form.email, extra_classes='ourClasses') }}
We can also use #John Go-Soco answers to use render_kw attribute on our form field, like this way.
2. The second way
style={'class': 'ourClasses', 'style': 'width:50%;'}
email = EmailField('Email',
validators=[InputRequired(), Length(1, 64), Email()],
render_kw=style)
But I would like more prefer to use the first way.
Pretty late though, but here is what I found. While rendering a template you can pass any key-value pairs inside the parenthesis, and it just puts those key values while rendering.
For example, if you had to put a class along with some placeholder text you can do it like below:
{{ form.email(class='custom-class' placeholder='email here') }}
will actually render like below:
<input class="custom-class" id="email" name="email" placeholder="email here" type="text" value="">
Basically, you can even try experimenting by adding some non-existent HTML attribute with some value and it gets rendered.
To make it a less of pain its good to have helper functions as macros and render them instead of actual fields directly.
Let's say you have common classes and error-classes.
You can have a helper macro like this:
{% macro render_field(field,cls,errcls) %}
{% if field.errors %}
<div class="form-group">
<label for="{{ field.id}}">{{ field.label.text }}</label>
{{ field(class = cls + " " + errcls,**kwargs) | safe}}
<div class="invalid-feedback">
{% for error in field.errors %}
<span> {{error}}</span>
{% endfor %}
</div>
</div>
{% else %}
<div class="form-group">
<label for="{{ field.id}}">{{ field.label.text }}</label>
{{ field(class = cls,**kwargs) | safe}}
</div>
{% endif %}
{% endmacro %}
Now while rendering a field you can call like this with additional attributes like this in the template:
{{render_field(form.email,cls="formcontrol",errcls="isinvalid",placeholder="Your Email") }}
Here I have used bootstrap classes, just modify the helper function to your needs!
Hope that helps! Happy coding!

CSS styling in Django forms

I would like to style the following:
forms.py:
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
email = forms.EmailField(required=False)
message = forms.CharField(widget=forms.Textarea)
contact_form.html:
<form action="" method="post">
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Submit">
</form>
For example, how do I set a class or ID for the subject, email, message to provide an external style sheet to?
Taken from my answer to:
How to markup form fields with <div class='field_type'> in Django
class MyForm(forms.Form):
myfield = forms.CharField(widget=forms.TextInput(attrs={'class': 'myfieldclass'}))
or
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['myfield'].widget.attrs.update({'class': 'myfieldclass'})
or
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
widgets = {
'myfield': forms.TextInput(attrs={'class': 'myfieldclass'}),
}
--- EDIT ---
The above is the easiest change to make to original question's code that accomplishes what was asked. It also keeps you from repeating yourself if you reuse the form in other places; your classes or other attributes just work if you use the Django's as_table/as_ul/as_p form methods. If you need full control for a completely custom rendering, this is clearly documented
-- EDIT 2 ---
Added a newer way to specify widget and attrs for a ModelForm.
This can be done using a custom template filter. Consider rendering your form this way:
<form action="/contact/" method="post">
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.subject.errors }}
{{ form.subject.label_tag }}
{{ form.subject }}
<span class="helptext">{{ form.subject.help_text }}</span>
</div>
</form>
form.subject is an instance of BoundField which has the as_widget() method.
You can create a custom filter addclass in my_app/templatetags/myfilters.py:
from django import template
register = template.Library()
#register.filter(name='addclass')
def addclass(value, arg):
return value.as_widget(attrs={'class': arg})
And then apply your filter:
{% load myfilters %}
<form action="/contact/" method="post">
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.subject.errors }}
{{ form.subject.label_tag }}
{{ form.subject|addclass:'MyClass' }}
<span class="helptext">{{ form.subject.help_text }}</span>
</div>
</form>
form.subjects will then be rendered with the MyClass CSS class.
If you don't want to add any code to the form (as mentioned in the comments to #shadfc's Answer), it is certainly possible, here are two options.
First, you just reference the fields individually in the HTML, rather than the entire form at once:
<form action="" method="post">
<ul class="contactList">
<li id="subject" class="contact">{{ form.subject }}</li>
<li id="email" class="contact">{{ form.email }}</li>
<li id="message" class="contact">{{ form.message }}</li>
</ul>
<input type="submit" value="Submit">
</form>
(Note that I also changed it to a unsorted list.)
Second, note in the docs on outputting forms as HTML, Django:
The Field id, is generated by
prepending 'id_' to the Field name.
The id attributes and tags are
included in the output by default.
All of your form fields already have a unique id. So you would reference id_subject in your CSS file to style the subject field. I should note, this is how the form behaves when you take the default HTML, which requires just printing the form, not the individual fields:
<ul class="contactList">
{{ form }} # Will auto-generate HTML with id_subject, id_email, email_message
{{ form.as_ul }} # might also work, haven't tested
</ul>
See the previous link for other options when outputting forms (you can do tables, etc).
Note - I realize this isn't the same as adding a class to each element (if you added a field to the Form, you'd need to update the CSS also) - but it's easy enough to reference all of the fields by id in your CSS like this:
#id_subject, #id_email, #email_message
{color: red;}
Per this blog post, you can add css classes to your fields using a custom template filter.
from django import template
register = template.Library()
#register.filter(name='addcss')
def addcss(field, css):
return field.as_widget(attrs={"class":css})
Put this in your app's templatetags/ folder and you can now do
{{field|addcss:"form-control"}}
You can do like this:
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
subject.widget.attrs.update({'id' : 'your_id'})
Hope that works.
Ignas
You could use this library: https://pypi.python.org/pypi/django-widget-tweaks
It allows you to do the following:
{% load widget_tweaks %}
<!-- add 2 extra css classes to field element -->
{{ form.title|add_class:"css_class_1 css_class_2" }}
Write your form like:
class MyForm(forms.Form):
name = forms.CharField(widget=forms.TextInput(attr={'class':'name'}),label="Your Name")
message = forms.CharField(widget=forms.Textarea(attr={'class':'message'}), label="Your Message")
In your HTML field do something like:
{% for field in form %}
<div class="row">
<label for="{{ field.name}}">{{ field.label}}</label>{{ field }}
</div>
{% endfor %}
Then in your CSS write something like:
.name{
/* you already have this class so create it's style form here */
}
.message{
/* you already have this class so create it's style form here */
}
label[for='message']{
/* style for label */
}
Hope this answer is worth a try! Note you must have written your views to render the HTML file that contains the form.
You can do:
<form action="" method="post">
<table>
{% for field in form %}
<tr><td>{{field}}</td></tr>
{% endfor %}
</table>
<input type="submit" value="Submit">
</form>
Then you can add classes/id's to for example the <td> tag. You can of course use any others tags you want. Check Working with Django forms as an example what is available for each field in the form ({{field}} for example is just outputting the input tag, not the label and so on).
Didn't see this one really...
https://docs.djangoproject.com/en/1.8/ref/forms/api/#more-granular-output
More granular output
The as_p(), as_ul() and as_table() methods are simply shortcuts for lazy developers – they’re not the only way a form object can be displayed.
class BoundField
Used to display HTML or access attributes for a single field of a Form instance.
The str() (unicode on Python 2) method of this object displays the HTML for this field.
To retrieve a single BoundField, use dictionary lookup syntax on your form using the field’s name as the key:
>>> form = ContactForm()
>>> print(form['subject'])
<input id="id_subject" type="text" name="subject" maxlength="100" />
To retrieve all BoundField objects, iterate the form:
>>> form = ContactForm()
>>> for boundfield in form: print(boundfield)
<input id="id_subject" type="text" name="subject" maxlength="100" />
<input type="text" name="message" id="id_message" />
<input type="email" name="sender" id="id_sender" />
<input type="checkbox" name="cc_myself" id="id_cc_myself" />
The field-specific output honors the form object’s auto_id setting:
>>> f = ContactForm(auto_id=False)
>>> print(f['message'])
<input type="text" name="message" />
>>> f = ContactForm(auto_id='id_%s')
>>> print(f['message'])
<input type="text" name="message" id="id_message" />
One solution is to use JavaScript to add the required CSS classes after the page is ready. For example, styling django form output with bootstrap classes (jQuery used for brevity):
<script type="text/javascript">
$(document).ready(function() {
$('#some_django_form_id').find("input[type='text'], select, textarea").each(function(index, element) {
$(element).addClass("form-control");
});
});
</script>
This avoids the ugliness of mixing styling specifics with your business logic.
You may not need to override your form class' __init__, because Django sets name & id attributes in the HTML inputs. You can have CSS like this:
form input[name='subject'] {
font-size: xx-large;
}
There is a very easy to install and great tool made for Django that I use for styling and it can be used for every frontend framework like Bootstrap, Materialize, Foundation, etc. It is called widget-tweaks Documentation: Widget Tweaks
You can use it with Django's generic views
Or with your own forms:
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
email = forms.EmailField(required=False)
message = forms.CharField(widget=forms.Textarea)
Instead of using default:
{{ form.as_p }} or {{ form.as_ul }}
You can edit it your own way using the render_field attribute that gives you a more html-like way of styling it like this example:
template.html
{% load widget_tweaks %}
<div class="container">
<div class="col-md-4">
{% render_field form.subject class+="form-control myCSSclass" placeholder="Enter your subject here" %}
</div>
<div class="col-md-4">
{% render_field form.email type="email" class+="myCSSclassX myCSSclass2" %}
</div>
<div class="col-md-4">
{% render_field form.message class+="myCSSclass" rows="4" cols="6" placeholder=form.message.label %}
</div>
</div>
This library gives you the opportunity to have well separated yout front end from your backend
In Django 1.10 (possibly earlier as well) you can do it as follows.
Model:
class Todo(models.Model):
todo_name = models.CharField(max_length=200)
todo_description = models.CharField(max_length=200, default="")
todo_created = models.DateTimeField('date created')
todo_completed = models.BooleanField(default=False)
def __str__(self):
return self.todo_name
Form:
class TodoUpdateForm(forms.ModelForm):
class Meta:
model = Todo
exclude = ('todo_created','todo_completed')
Template:
<form action="" method="post">{% csrf_token %}
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.todo_name.errors }}
<label for="{{ form.name.id_for_label }}">Name:</label>
{{ form.todo_name }}
</div>
<div class="fieldWrapper">
{{ form.todo_description.errors }}
<label for="{{ form.todo_description.id_for_label }}">Description</label>
{{ form.todo_description }}
</div>
<input type="submit" value="Update" />
</form>
For larger form instead of writing css classed for every field you could to this
class UserRegistration(forms.ModelForm):
# list charfields
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'password', 'password2')
def __init__(self, *args, **kwargs):
super(UserRegistration, self).__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs['class'] = 'form-control'
Edit: Another (slightly better) way of doing what I'm suggesting is answered here: Django form input field styling
All the above options are awesome, just thought I'd throw in this one because it's different.
If you want custom styling, classes, etc. on your forms, you can make an html input in your template that matches your form field. For a CharField, for example, (default widget is TextInput), let's say you want a bootstrap-looking text input. You would do something like this:
<input type="text" class="form-control" name="form_field_name_here">
And as long as you put the form field name matches the html name attribue, (and the widget probably needs to match the input type as well) Django will run all the same validators on that field when you run validate or form.is_valid() and
Styling other things like labels, error messages, and help text don't require much workaround because you can do something like form.field.error.as_text and style them however you want. The actual fields are the ones that require some fiddling.
I don't know if this is the best way, or the way I would recommend, but it is a way, and it might be right for someone.
Here's a useful walkthrough of styling forms and it includes most of the answers listed on SO (like using the attr on the widgets and widget tweaks).
https://simpleisbetterthancomplex.com/article/2017/08/19/how-to-render-django-form-manually.html
Styling widget instances
If you want to make one widget instance look different from another, you will need to specify additional attributes at the time when the widget object is instantiated and assigned to a form field (and perhaps add some rules to your CSS files).
https://docs.djangoproject.com/en/2.2/ref/forms/widgets/
To do this, you use the Widget.attrs argument when creating the widget:
class CommentForm(forms.Form):
name = forms.CharField(widget=forms.TextInput(attrs={'class': 'special'}))
url = forms.URLField()
comment = forms.CharField(widget=forms.TextInput(attrs={'size': '40'}))
You can also modify a widget in the form definition:
class CommentForm(forms.Form):
name = forms.CharField()
url = forms.URLField()
comment = forms.CharField()
name.widget.attrs.update({'class': 'special'})
comment.widget.attrs.update(size='40')
Or if the field isn’t declared directly on the form (such as model form fields), you can use the Form.fields attribute:
class CommentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['name'].widget.attrs.update({'class': 'special'})
self.fields['comment'].widget.attrs.update(size='40')
Django will then include the extra attributes in the rendered output:
>>> f = CommentForm(auto_id=False)
>>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" class="special" required></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" required></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" size="40" required></td></tr>
I was playing around with this solution to maintain consistency throughout the app:
def bootstrap_django_fields(field_klass, css_class):
class Wrapper(field_klass):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def widget_attrs(self, widget):
attrs = super().widget_attrs(widget)
if not widget.is_hidden:
attrs["class"] = css_class
return attrs
return Wrapper
MyAppCharField = bootstrap_django_fields(forms.CharField, "form-control")
Then you don't have to define your css classes on a form by form basis, just use your custom form field.
It's also technically possible to redefine Django's forms classes on startup like so:
forms.CharField = bootstrap_django_fields(forms.CharField, "form-control")
Then you could set the styling globally even for apps not in your direct control. This seems pretty sketchy, so I am not sure if I can recommend this.

Resources