django-tapeforms¶
All Contents¶
Installation¶
django-tapeforms supports Python 3 only and requires at least Django 2.2. No other dependencies are required.
To start, simply install the latest stable package using the command
$ pip install django-tapeforms
In addition, you have to add 'tapeforms'
to the INSTALLED_APP
setting
in your settings.py
.
Thats it, now continue to the Usage section to learn how to render your forms to HTML.
Usage¶
Tapeforms provides a Mixin to add the required functionality to the Django forms which is required for rendering the whole form and its fields using Django templates.
from django import forms
from tapeforms.mixins import TapeformMixin
class ContactForm(TapeformMixin, forms.Form):
name = forms.CharField()
email = forms.EmailField()
text = forms.CharField(widget=forms.Textarea)
Together with some template code you get some beautiful rendered forms.
{% load tapeforms %}
<form method="post" action=".">
{% csrf_token %}
{% form form %}
<button type="submit">Submit</button>
</form>
By default, the form will be rendered using the tapeforms/layouts/default.html
.
To learn how to override the template which is used, please refer to the
Advanced usage section.
If you don’t want to override the template, you might also use the as_tapeform shortcut.
<form method="post" action=".">
{% csrf_token %}
{{ form.as_tapeform }}
<button type="submit">Submit</button>
</form>
Note
For a full overview of the methods you might override to customize the rendering of your forms, go to the API Reference for mixins.
Advanced usage¶
Most of the advanced features are also demo’ed in the examples project you can find
in the django-tapeforms codebase. Go to the examples
directory.
Note
Most class properties have a corresponding method to access the value. This helps in cases where you might want to manipulate the response in a more dynamic way. If you’re unsure what can be changed, refer to the API Reference.
Overriding layout templates¶
In the context of django-tapeforms the layout template is the outer part of a form which contains the non field errors and the loop over the fields to render.
If you want to override the template used for rendering the form layout, the best way is defining a property on the form class:
class MyForm(TapeformMixin, forms.Form):
layout_template = 'another-form-template.html'
field1 = forms.CharField()
You are done. The form
template tag will pick the new
defined template for rendering.
If you need to select the layout template on other things like the instance
in ModelForm
, you can overwrite the get_layout_template
method.
Overriding field templates¶
In the context of django-tapeforms the field template is the part of a form which contains the label, widget, errors and help text for a certain field.
If you want to override the template used for rendering fields, there is more than one way.
When the template should be changed for all fields, you can simply set the
field_template
property.
class MyForm(TapeformMixin, forms.Form):
field_template = 'my-field-template.html'
field1 = forms.CharField()
The formfield
template tag will now pick the new
defined template for rendering the fields.
You can define the templates used for field rendering in a more specific way.
class MyForm(TapeformMixin, forms.Form):
field_template_overrides = {
'field1': 'my-field1-template.html',
forms.IntegerField: 'my-number-template.html',
}
field1 = forms.CharField()
field2 = forms.IntegerField()
field3 = forms.IntegerField()
As you can see, you can override the templates for fields based on the field name and also based on the field class.
If you need to select the field template depending on other things, you can
overwrite the get_field_template
method. It receives a
BoundField
instance for the template selection.
Overriding widget templates¶
In the context of django-tapeforms and Django itself, the widget template is used for the actual input element.
If you want to override the template used for rendering widgets, you can change
the template_name
by subclassing the widget classes but this requires much effort.
To make this easier, the TapeformMixin provided a helper to set the widget
template_name
. The matching is done using the field name and widget class.
class MyForm(TapeformMixin, forms.Form):
widget_template_overrides = {
'field1': 'my-field1-widget-template.html',
forms.NumberInput: 'my-number-widget-template.html',
}
field1 = forms.CharField()
field2 = forms.IntegerField()
field3 = forms.IntegerField()
If you need to select the widget template based on other things, you can overwrite
the get_widget_template
method. It receives the
field name as str
and the Field
instance.
Changing the applied CSS classes¶
When you render the form using django-tapeforms you can apply CSS classes to the field container, label and widget.
This is done using the properties field_container_css_class
,
field_label_css_class
and widget_css_class
.
For all CSS class properties, there are methods to override the applied CSS class per field. Please refer to the API Reference to learn what arguments are passed to the CSS class methods.
Adding CSS classes to invalid field¶
When you render the form using django-tapeforms, you can also apply additional CSS classes to the label and widget of a field which has errors.
This is done using the properties field_label_invalid_css_class
and
widget_invalid_css_class
.
If you need to set more attributes to the widget when there are errors, you can
overwrite the apply_widget_invalid_options
method. It
receives the field name as str
.
Fieldsets¶
Learn how to organize your form fields in multiple fieldsets and have them rendered nicely.
Manual fieldsets¶
The first way to get fieldsets for organizing your form’s fields is using the
TapeformFieldset
directly. This is also the low-level approach with full
control of whats happening.
class MyForm(TapeformMixin, forms.Form):
field1 = forms.CharField()
field2 = forms.CharField()
field3 = forms.CharField()
field4 = forms.CharField(widget=forms.HiddenInput)
def first_fieldset(self):
return TapeformFieldset(self, fields=('field1', 'field2'), primary=True)
def second_fieldset(self):
return TapeformFieldset(self, exclude=('field1', 'field1'))
Note
You have to make sure that at least one fieldset is marked as your primary fieldsets. This is required because only the primary fieldset will render the non field errors and all hidden fields of the form.
The fieldsets can then be rendered using the form
template tag just like classic forms.
<form action="." method="post" >
{% csrf_token %}
<fieldset>
{% form form.first_fieldset %}
</fieldset>
<fieldset>
{% form form.second_fieldset %}
</fieldset>
<button type="submit">Submit</button>
</form>
Generated fieldsets¶
It might come to your mind that defining alle the fieldsets using methods is a bit to much boilerplate.
Because of this, django-tapeforms provides another Mixin you can use to make generation of fieldsets a lot easier.
Lets have a look on an example.
class MyForm(TapeformMixin, forms.Form):
field1 = forms.CharField()
field2 = forms.CharField()
field3 = forms.CharField()
field4 = forms.CharField(widget=forms.HiddenInput)
fieldsets = [
{'fields': ('field1', 'field2')},
{'exclude': ('field1', 'field2')}, # Render all remaining fields
]
Also, the template is simpler now.
<form action="." method="post" >
{% csrf_token %}
{% for fieldset in form.get_fieldsets %}
<fieldset>
{% form fieldset %}
</fieldset>
{% endfor %}
<button type="submit">Submit</button>
</form>
While the difference in code written might not be that big when rendering two fieldsets, imagine the difference when having lets say 7 oder 8 fieldsets.
As you can see, we don’t have to care for the primary flag anymore. The get_fieldsets
methods make sure that one fieldset is the primary fieldset (by default, the first fieldset
is marked as primary).
There are many methods in the TapeformFieldsetsMixin you can override to get your hands on the generation process (like selection the right fieldset class or manipulating the data which is used to instantiate the fieldset).
It is also possible to generate the fieldsets configuration on the fly by overriding
the get_fieldsets
method and pass a config to your super call.
class MyForm(TapeformMixin, forms.Form):
field1 = forms.CharField()
field2 = forms.CharField()
field3 = forms.CharField()
field4 = forms.CharField(widget=forms.HiddenInput)
def get_fieldsets(self):
# Geneate a fieldset for every form field. Why would one do that?
return super().get_fieldsets([
{'fields': (field.name,)}
for field in self.visible_fields()
])
Passing around additional data¶
In more compex setups you might want to pass around additional data. In our example we assume that we require a css class added to the fieldset element.
class MyForm(TapeformMixin, forms.Form):
field1 = forms.CharField()
field2 = forms.CharField()
field3 = forms.CharField()
field4 = forms.CharField(widget=forms.HiddenInput)
fieldsets = [{
'fields': ('field1', 'field2'),
'extra': {'css_class': 'my-class-foo'}
}, {
'exclude': ('field1', 'field2'),
'extra': {'css_class': 'my-class-bar'}
}]
<form action="." method="post" >
{% csrf_token %}
{% for fieldset in form.get_fieldsets %}
<fieldset class="{{ fieldset.extra.css_class }}">
{% form fieldset %}
</fieldset>
{% endfor %}
<button type="submit">Submit</button>
</form>
The extra key in the fieldset configuration is not checked in any way. Its just passed
around. You might use it to carry things in a dic
like in the example or push
a model instance to the template for further use.
Advanced usage¶
For a full overview of the methods TapeformFieldset and TapeformFieldsetsMixin provide, go to the API Reference for fieldsets.
Contrib¶
Extra, optional, features of django-tapeforms.
Bootstrap mixin¶
You can use the tapeforms.contrib.bootstrap.Bootstrap4TapeformMixin
to render forms with a Bootstrap 4 compatible HTML layout / CSS classes, or
tapeforms.contrib.bootstrap.Bootstrap5TapeformMixin
for Bootstrap 5.
This alternative mixin makes sure that the rendered widgets, fields and labels have the correct css classes assigned.
In addition, the mixin uses a different template for the fields because Bootstrap requires that the ordering of label and widget inside a field is swapped (widget first, label second).
Foundation mixin¶
You can use the tapeforms.contrib.foundation.FoundationTapeformMixin
to render forms with a Foundation compatible HTML layout / css classes.
This alternative mixin makes sure that the rendered widgets, fields and labels have the correct CSS classes assigned especially in case of errors.
In addition, the mixin uses a different template for the fields. It is required
to swap the ordering of label and widget when rendering a checkbox, and also to
wrap mulitple inputs - e.g. a group of checkboxes or radio buttons - in a
fieldset
element, as Foundation documentation suggests.
API Reference¶
Mixins¶
- class tapeforms.mixins.TapeformLayoutMixin[source]¶
Bases:
object
Mixin to render a form of fieldset as HTML.
- get_layout_template(template_name=None)[source]¶
Returns the layout template to use when rendering the form to HTML.
Preference of template selection:
Provided method argument template_name
Form class property layout_template
Globally defined default template from defaults.LAYOUT_DEFAULT_TEMPLATE
- Parameters:
template_name – Optional template to use instead of other configurations.
- Returns:
Template name to use when rendering the form.
- get_layout_context()[source]¶
Returns the context which is used when rendering the form to HTML.
The generated template context will contain the following variables:
form: Form instance
errors: ErrorList instance with non field errors and hidden field errors
hidden_fields: All hidden fields to render.
visible_fields: All visible fields to render.
- Returns:
Template context for form rendering.
- class tapeforms.mixins.TapeformMixin(*args, **kwargs)[source]¶
Bases:
TapeformLayoutMixin
Mixin to extend the forms capability to render itself as HTML output. (using the template tags provided by tapeforms).
- field_template_overrides = None[source]¶
A dictionary of form-field names and/or form-field classes to override the field template which is used when rendering a certain form-field. Optional.
- field_container_css_class = 'form-field'[source]¶
The CSS class to apply to the form-field container element.
- field_label_css_class = None[source]¶
CSS class to append to the rendered field label tag. Optional.
- field_label_invalid_css_class = None[source]¶
An additional CSS class to append to the rendered field label tag when the field has errors. Optional.
- widget_template_overrides = None[source]¶
A dictionary of form-field names and/or widget classes to override the widget template which is used when rendering a certain form-field. Optional.
- widget_invalid_css_class = None[source]¶
An additional CSS class to append to the widget attributes when the field has errors. Optional.
- __init__(*args, **kwargs)[source]¶
The init method is overwritten to apply widget templates and CSS classes.
- full_clean(*args, **kwargs)[source]¶
The full_clean method is hijacked to apply special treatment to invalid field inputs. For example adding extra options/classes to widgets.
- get_field_template(bound_field, template_name=None)[source]¶
Returns the field template to use when rendering a form field to HTML.
Preference of template selection:
Provided method argument template_name
Template from field_template_overrides selected by field name
Template from field_template_overrides selected by field class
Form class property field_template
Globally defined default template from defaults.LAYOUT_FIELD_TEMPLATE
- Parameters:
bound_field – BoundField instance to select a template for.
template_name – Optional template to use instead of other configurations.
- Returns:
Template name to use when rendering the form field.
- get_field_container_css_class(bound_field)[source]¶
Returns the container CSS class to use when rendering a field template.
By default, returns the Form class property field_container_css_class.
- Parameters:
bound_field – BoundField instance to return CSS class for.
- Returns:
A CSS class string.
- get_field_label_css_class(bound_field)[source]¶
Returns the optional label CSS class to use when rendering a field template.
By default, returns the Form class property field_label_css_class. If the field has errors and the Form class property field_label_invalid_css_class is defined, its value is appended to the CSS class.
- Parameters:
bound_field – BoundField instance to return CSS class for.
- Returns:
A CSS class string or None
- get_field_context(bound_field)[source]¶
Returns the context which is used when rendering a form field to HTML.
The generated template context will contain the following variables:
form: Form instance
field: BoundField instance of the field
field_id: Field ID to use in <label for=”..”>
field_name: Name of the form field to render
errors: ErrorList instance with errors of the field
required: Boolean flag to signal if the field is required or not
label: The label text of the field
label_css_class: The optional label CSS class, might be None
help_text: Optional help text for the form field. Might be None
container_css_class: The CSS class for the field container.
widget_class_name: Lowercased version of the widget class name (e.g. ‘textinput’)
widget_input_type: input_type property of the widget instance, falls back to widget_class_name if not available.
- Returns:
Template context for field rendering.
- apply_widget_options(field_name)[source]¶
Applies additional widget options like changing the input type of DateInput and TimeInput to “date” / “time” to enable Browser date pickers or other attributes/properties.
- apply_widget_template(field_name)[source]¶
Applies widget template overrides if available.
The method uses the get_widget_template method to determine if the widget template should be exchanged. If a template is available, the template_name property of the widget instance is updated.
- Parameters:
field_name – A field name of the form.
- get_widget_template(field_name, field)[source]¶
Returns the optional widget template to use when rendering the widget for a form field.
- Preference of template selection:
Template from widget_template_overrides selected by field name
Template from widget_template_overrides selected by widget class
By default, returns None which means “use Django’s default widget template”.
- Parameters:
field_name – The field name to select a widget template for.
field – Field instance to return a widget template.
- Returns:
Template name to use when rendering the widget or None
- apply_widget_css_class(field_name)[source]¶
Applies CSS classes to widgets if available.
The method uses the get_widget_css_class method to determine if the widget CSS class should be changed. If a CSS class is returned, it is appended to the current value of the class property of the widget instance.
- Parameters:
field_name – A field name of the form.
- get_widget_css_class(field_name, field)[source]¶
Returns the optional widget CSS class to use when rendering the form’s field widget.
By default, returns None which means “no CSS class / no change”.
- Parameters:
field_name – The field name of the corresponding field for the widget.
field – Field instance to return CSS class for.
- Returns:
A CSS class string or None
- apply_widget_invalid_options(field_name)[source]¶
Applies additional widget options for an invalid field.
This method is called when there is some error on a field to apply additional options on its widget. It does the following:
Sets the aria-invalid property of the widget for accessibility.
Adds an invalid CSS class, which is determined by the returned value of get_widget_invalid_css_class method. If a CSS class is returned, it is appended to the current value of the class property of the widget.
- Parameters:
field_name – A field name of the form.
- get_widget_invalid_css_class(field_name, field)[source]¶
Returns the optional widget CSS class to append when rendering the form’s field widget in case of error.
By default, returns None which means “no CSS class / no change”.
- Parameters:
field_name – The field name of the corresponding field for the widget.
field – Field instance to return CSS class for.
- Returns:
A CSS class string or None
Fieldsets¶
- class tapeforms.fieldsets.TapeformFieldset(form, fields=None, exclude=None, primary=False, template=None, extra=None)[source]¶
Bases:
TapeformLayoutMixin
,object
Class to render a subset of a form’s fields. From a template perspective, a fieldset looks quite similar to a form (and can use the same template tag to render:
form
.- __init__(form, fields=None, exclude=None, primary=False, template=None, extra=None)[source]¶
Initializes a fieldset instance to be used like a form in a template.
Just like in ModelForm Meta, you have to provide at least a list of fields to render in this fieldset or a list of fields to exclude. If you provide both, exclusions have a higher priority.
- Parameters:
form – The form instance to take fields from.
fields – A list of visible field names to include in this fieldset.
exclude – A list of visible fields to _not_ include in this fieldset.
primary – If the fieldset is primary, this fieldset is responsible for rendering the hidden fields and non field errors.
template – You can provide an alternative layout template to use.
extra – This argument is carried around with the fieldset and is also available in the template. Useful to pass some special arguments for rendering around (like a fieldset headline.
- Returns:
A configured fieldset instance.
Returns the hidden fields of the form for rendering of the fieldset is marked as the primary fieldset.
- Returns:
List of bound field instances or empty tuple.
- non_field_errors()[source]¶
Returns all non-field errors of the form for rendering of the fieldset is marked as the primary fieldset.
- Returns:
ErrorList instance with non field errors or empty ErrorList.
- visible_fields()[source]¶
Returns the reduced set of visible fields to output from the form.
This method respects the provided
fields
configuration _and_ exlcudes all fields from theexclude
configuration.If no
fields
where provided when configuring this fieldset, all visible fields minus the excluded fields will be returned.- Returns:
List of bound field instances or empty tuple.
- class tapeforms.fieldsets.TapeformFieldsetsMixin[source]¶
Bases:
object
Mixin to generate fieldsets based on the fieldsets property of a
TapeformFieldsetsMixin
enabled form.- fieldset_class[source]¶
Default fieldset class to use when instantiating a fieldset.
alias of
TapeformFieldset
- get_fieldset_class(**fieldset_kwargs)[source]¶
Returns the fieldset class to use when generating the fieldset using the passed fieldset kwargs.
- Parameters:
fieldset_kwargs –
dict
with the fieldset config fromfieldsets
- Returns:
Class to use when instantiating the fieldset.
- get_fieldset(**fieldset_kwargs)[source]¶
Returns a fieldset instance for the passed
fieldset_kwargs
.- Parameters:
fieldset_kwargs –
dict
with the fieldset config fromfieldsets
- Returns:
Fieldset instance
- get_fieldsets(fieldsets=None)[source]¶
This method returns a generator which yields fieldset instances.
The method uses the optional fieldsets argument to generate fieldsets for. If no fieldsets argument is passed, the class property
fieldsets
is used.When generating the fieldsets, the method ensures that at least one fielset will be the primary fieldset which is responsible for rendering the non field errors and hidden fields.
- Parameters:
fieldsets – Alternative set of fieldset kwargs. If passed this set is prevered of the
fieldsets
property of the form.- Returns:
generator which yields fieldset instances.
Contrib¶
Bootstrap mixin¶
- class tapeforms.contrib.bootstrap.Bootstrap4TapeformMixin(*args, **kwargs)[source]¶
Bases:
TapeformMixin
Tapeform Mixin to render Bootstrap v4 compatible forms. (using the template tags provided by tapeforms).
- layout_template = 'tapeforms/layouts/bootstrap.html'[source]¶
Use a special layout template for Bootstrap compatible forms.
- field_template = 'tapeforms/fields/bootstrap.html'[source]¶
Use a special field template for Bootstrap compatible forms.
- field_container_css_class = 'form-group'[source]¶
All form field containers need a CSS class “form-group”.
- widget_template_overrides = {<class 'django.forms.widgets.SelectDateWidget'>: 'tapeforms/widgets/bootstrap_multiwidget.html', <class 'django.forms.widgets.SplitDateTimeWidget'>: 'tapeforms/widgets/bootstrap_multiwidget.html', <class 'django.forms.widgets.RadioSelect'>: 'tapeforms/widgets/bootstrap_multipleinput.html', <class 'django.forms.widgets.CheckboxSelectMultiple'>: 'tapeforms/widgets/bootstrap_multipleinput.html'}[source]¶
Widgets with multiple inputs require some extra care (don’t use ul, etc.)
- get_field_container_css_class(bound_field)[source]¶
Returns “form-check” if widget is CheckboxInput in addition of the default value from the form property (“form-group”) - which is returned for all other fields.
- class tapeforms.contrib.bootstrap.Bootstrap5TapeformMixin(*args, **kwargs)[source]¶
Bases:
Bootstrap4TapeformMixin
Tapeform Mixin to render Bootstrap v5 compatible forms. (using the template tags provided by tapeforms).
- field_container_css_class = 'mb-3'[source]¶
Apply the CSS class “mb-3” to add spacing between the form fields.
- widget_template_overrides = {<class 'django.forms.widgets.SelectDateWidget'>: 'tapeforms/widgets/bootstrap5_multiwidget.html', <class 'django.forms.widgets.SplitDateTimeWidget'>: 'tapeforms/widgets/bootstrap5_multiwidget.html', <class 'django.forms.widgets.RadioSelect'>: 'tapeforms/widgets/bootstrap_multipleinput.html', <class 'django.forms.widgets.CheckboxSelectMultiple'>: 'tapeforms/widgets/bootstrap_multipleinput.html'}[source]¶
Widgets with multiple inputs require some extra care (don’t use ul, etc.)
- tapeforms.contrib.bootstrap.BootstrapTapeformMixin[source]¶
This alias is for backward compatibility only. It could be deprecated and removed at some time, you should use
Bootstrap4TapeformMixin
orBootstrap5TapeformMixin
instead.
Foundation mixin¶
- class tapeforms.contrib.foundation.FoundationTapeformMixin(*args, **kwargs)[source]¶
Bases:
TapeformMixin
Tapeform Mixin to render Foundation compatible forms. (using the template tags provided by tapeforms).
- layout_template = 'tapeforms/layouts/foundation.html'[source]¶
Use a special layout template for Foundation compatible forms.
- field_template = 'tapeforms/fields/foundation.html'[source]¶
Use a special field template for Foundation compatible forms.
- field_label_invalid_css_class = 'is-invalid-label'[source]¶
Use a special class to invalid field’s label.
- widget_invalid_css_class = 'is-invalid-input'[source]¶
Use a special class to invalid field’s widget.
- widget_template_overrides = {<class 'django.forms.widgets.RadioSelect'>: 'tapeforms/widgets/foundation_multipleinput.html', <class 'django.forms.widgets.CheckboxSelectMultiple'>: 'tapeforms/widgets/foundation_multipleinput.html'}[source]¶
Widgets with multiple inputs require some extra care (don’t use ul, etc.)
Changelog¶
1.2.0 - 2023-02-25¶
Replace removed ‘.form-row’ by grid system in Bootstrap v5 multiple widgets
Add ‘.is-invalid’ to multiple widget container in Bootstrap mixins to display the ‘.invalid-feedback’ elements
Drop “official” support for Python < 3.8, add support for Django 4.1
1.1.0 - 2021-09-13¶
Rename Bootstrap mixin to
Bootstrap4TapeformMixin
, an alias is kept for backward compatibility but it will be deprecated and removed at some timeRemove ‘small’ CSS class from the help text in Boostrap mixin
Use ‘form-field-’ as prefix for container CSS class instead of the value returned by
get_field_container_css_class
Add a new
Bootstrap5TapeformMixin
mixin for Bootstrap v5Add ‘form-group’ CSS class to checkbox container in Bootstrap mixin
1.0.1 - 2021-04-28¶
Add support for Django 3
1.0.0 - 2021-03-15¶
Add support for Python 3.7 and 3.8
Remove support for Django 2.0 and 2.1
Fix duplicated class issue in bootstrap template
0.2.1 - 2019-02-06¶
Change the way extra options are applied to invalid widgets
0.2.0 - 2018-12-12¶
Add support for Foundation flavored forms
Improve support for applying css classes to invalid fields
0.1.1 - 2018-08-27¶
Fix styling of invalid inputs in Bootstrap forms by adding the correct css class
0.1.0 - 2018-08-17¶
Add
as_tapeform
method to Forms to render forms without the need to call theform
template tagAdd hook to update widget options
apply_widget_options
DateInput, TimeInput and SplitDateTimeWidget get a proper input type to activate Browser’s datepicker.
Fix invalid help text display if html tags are part of the help text
0.0.4 - 2018-03-22¶
Bugfix release (invalid template path)
0.0.3 - 2018-03-22¶
Improved Bootstrap 4 support
0.0.2 - 2018-03-11¶
Allow ModelForms in form template tag.
0.0.1 - 2018-02-27¶
Initial release of django-tapeforms