1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
from copy import deepcopy
from django import forms
from django.forms.fields import FileField, ImageField
from django.utils.translation import gettext_lazy as _
from django.urls import reverse, NoReverseMatch
from crispy_forms.helper import FormHelper
from .layout import Submit, HTML, InlineSwitchField
class FoundationFormMixin(object):
"""
Mixin to implement a layout helper that will automatically build a form
layout.
Generally, you will prefer to use ``FoundationForm`` or
``FoundationModelForm`` instead.
If you still want to directly use this mixin you'll just have to execute
``FoundationFormMixin.init_helper()`` in your form init.
**Attributes**
title
If set, defines the form's title
layout
If set, override the default layout for the form
error_title
Defines the error title for non field errors
form_id
Defines the id of the form
classes
Defines the classes used on the form
action
Defines the action of the form. ``reverse`` will be called on the
value. On failure the value will be assigned as is
method
Defines the method used for the action
attrs
Defines the attributes of the form
switches
If True, will replace all fields checkboxes with switches
submit
Adds a submit button on the form. Can be set to a Submit object or
a string which will be used as the value of the submit button
title_templatestring
Template string used to display form title (if any)
"""
title = None
layout = None
error_title = _("Errors :")
form_id = None
classes = "foundation-form"
action = ""
method = "post"
attrs = {}
switches = True
submit = True
title_templatestring = u"<h3 class=\"subheader\">{0}</h3>"
def init_helper(self):
# Put required HTML attribute on required fields so they are managed by
# Abide (if enabled)
if "data_abide" in self.attrs:
for field_name, field in self.fields.items():
if hasattr(self, 'instance'):
field_value = getattr(self.instance, field_name, None)
else:
field_value = None
if field.required \
and not ((isinstance(field, FileField) or
isinstance(field, ImageField))
and field_value):
field.widget.attrs["required"] = ""
field.abide_msg = _("This field is required.")
if not self.layout:
# Start with an empty layout
self.helper = FormHelper(self)
else:
# Start from the given layout
self.helper = FormHelper()
self.helper.layout = deepcopy(self.layout)
# Try to reverse form_action url, else fallback to use it as a simple
# string
try:
self.helper.form_action = reverse(self.action)
except NoReverseMatch:
self.helper.form_action = self.action
if self.title:
html = HTML(self.title_templatestring.format(self.title))
self.helper.layout.insert(0, html)
if self.form_id is not None:
self.helper.form_id = self.form_id
self.helper.form_class = self.classes
self.helper.form_method = self.method
self.helper.form_error_title = self.error_title
self.helper.attrs = self.attrs
if self.switches:
# Get a list of all fields with their location within the layout
layout_field_names = self.helper.layout.get_field_names()
# Transform checkbox fields to switches element
for pointer in layout_field_names:
if isinstance(self.fields[pointer[1]].widget,
forms.CheckboxInput):
field = InlineSwitchField(pointer[1],
switch_class="inline")
self.replace_layout_object(pointer[0], field)
if self.submit:
if isinstance(self.submit, Submit):
self.helper.add_input(self.submit)
elif isinstance(self.submit, str):
self.helper.add_input(Submit('submit', self.submit))
else:
self.helper.add_input(Submit('submit', _("Submit")))
def replace_layout_object(self, position, instead):
previous_layout_object = None
layout_object = self.helper.layout.fields[position[0]]
for i in position[1:]:
previous_layout_object = layout_object
layout_object = layout_object.fields[i]
if previous_layout_object:
previous_layout_object[-1] = instead
else:
self.helper.layout.fields[position[0]] = instead
class FoundationForm(FoundationFormMixin, forms.Form):
"""
A **Django form** that inherit from ``FoundationFormMixin`` to
automatically build a form layout
Example:
.. code-block:: python
from django import forms
from crispy_forms_foundation.forms import FoundationForm
class YourForm(FoundationForm):
title = "Testing"
action = 'test'
layout = Layout(Fieldset("Section", "my_field", "my_field_2"))
switches = False
attrs = {'data_abide': ""}
title = forms.CharField(label='Title', required=True)
slug = forms.CharField(label='Slug', required=False)
"""
def __init__(self, *args, **kwargs):
super(FoundationForm, self).__init__(*args, **kwargs)
self.init_helper()
class FoundationModelForm(FoundationFormMixin, forms.ModelForm):
"""
A **Django Model form** that inherit from ``FoundationFormMixin`` to
automatically build a form layout
Example:
.. code-block:: python
from crispy_forms_foundation.forms import FoundationModelForm
class YourForm(FoundationModelForm):
title = "Testing"
action = 'test'
layout = Layout(Fieldset("Section", "my_field", "my_field_2"))
switches = False
attrs = {'data_abide': ""}
class Meta:
model = MyModel
fields = ['my_field', 'my_field_2', 'my_field_3']
"""
def __init__(self, *args, **kwargs):
super(FoundationModelForm, self).__init__(*args, **kwargs)
self.init_helper()
|