From 6c0ffe2f417bdd6463d87905844ddb86b112cc96 Mon Sep 17 00:00:00 2001 From: David Smith <39445562+smithdc1@users.noreply.github.com> Date: Wed, 26 Feb 2020 20:07:34 +0000 Subject: [PATCH] Apply Black to entire codebase (#990) * Apply Black to entire codebase * Add Black GitHub Action --- .github/workflows/black.yml | 22 + README.rst | 3 + crispy_forms/__init__.py | 2 +- crispy_forms/base.py | 1 + crispy_forms/bootstrap.py | 180 ++--- crispy_forms/exceptions.py | 1 + crispy_forms/helper.py | 141 ++-- crispy_forms/layout.py | 158 +++-- crispy_forms/layout_slice.py | 32 +- .../templatetags/crispy_forms_field.py | 57 +- .../templatetags/crispy_forms_filters.py | 56 +- .../templatetags/crispy_forms_tags.py | 97 ++- .../templatetags/crispy_forms_utils.py | 6 +- crispy_forms/tests/conftest.py | 31 +- crispy_forms/tests/forms.py | 73 +- crispy_forms/tests/test_dynamic_api.py | 413 ++++-------- crispy_forms/tests/test_form_helper.py | 628 +++++++++--------- crispy_forms/tests/test_layout.py | 553 ++++++--------- crispy_forms/tests/test_layout_objects.py | 276 ++++---- crispy_forms/tests/test_settings.py | 53 +- crispy_forms/tests/test_tags.py | 144 ++-- crispy_forms/tests/test_utils.py | 12 +- crispy_forms/tests/urls.py | 2 +- crispy_forms/tests/utils.py | 10 +- crispy_forms/utils.py | 65 +- pyproject.toml | 3 + setup.cfg | 3 +- 27 files changed, 1358 insertions(+), 1664 deletions(-) create mode 100644 .github/workflows/black.yml create mode 100644 pyproject.toml diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml new file mode 100644 index 000000000..e10c93d02 --- /dev/null +++ b/.github/workflows/black.yml @@ -0,0 +1,22 @@ +name: black + +on: [push, pull_request] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v1 + with: + python-version: 3.8 + - name: Install Black + run: | + python -m pip install --upgrade pip + pip install Black + - name: run black + run: | + black crispy_forms --check diff --git a/README.rst b/README.rst index a4a8a4197..724eaa6e9 100644 --- a/README.rst +++ b/README.rst @@ -6,6 +6,9 @@ django-crispy-forms :alt: Build Status :target: https://travis-ci.org/django-crispy-forms/django-crispy-forms +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + .. image:: http://codecov.io/github/django-crispy-forms/django-crispy-forms/coverage.svg?branch=master :target: http://codecov.io/github/django-crispy-forms/django-crispy-forms?branch=master diff --git a/crispy_forms/__init__.py b/crispy_forms/__init__.py index e8b6b090b..2d986fc50 100644 --- a/crispy_forms/__init__.py +++ b/crispy_forms/__init__.py @@ -1 +1 @@ -__version__ = '1.8.1' +__version__ = "1.8.1" diff --git a/crispy_forms/base.py b/crispy_forms/base.py index 7eba074ce..87c479aec 100644 --- a/crispy_forms/base.py +++ b/crispy_forms/base.py @@ -8,6 +8,7 @@ class KeepContext: Layout objects should use `extra_context` to introduce context variables, never touch context object themselves, that could introduce side effects. """ + def __init__(self, context, keys): self.context = context self.keys = keys diff --git a/crispy_forms/bootstrap.py b/crispy_forms/bootstrap.py index 32ab86807..8a24fc8ea 100644 --- a/crispy_forms/bootstrap.py +++ b/crispy_forms/bootstrap.py @@ -15,48 +15,56 @@ def __init__(self, field, prepended_text=None, appended_text=None, *args, **kwar self.field = field self.appended_text = appended_text self.prepended_text = prepended_text - if 'active' in kwargs: - self.active = kwargs.pop('active') + if "active" in kwargs: + self.active = kwargs.pop("active") self.input_size = None - css_class = kwargs.get('css_class', '') - if 'input-lg' in css_class: - self.input_size = 'input-lg' - if 'input-sm' in css_class: - self.input_size = 'input-sm' + css_class = kwargs.get("css_class", "") + if "input-lg" in css_class: + self.input_size = "input-lg" + if "input-sm" in css_class: + self.input_size = "input-sm" super().__init__(field, *args, **kwargs) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, extra_context=None, **kwargs): extra_context = extra_context.copy() if extra_context is not None else {} - extra_context.update({ - 'crispy_appended_text': self.appended_text, - 'crispy_prepended_text': self.prepended_text, - 'input_size': self.input_size, - 'active': getattr(self, "active", False) - }) - if hasattr(self, 'wrapper_class'): - extra_context['wrapper_class'] = self.wrapper_class + extra_context.update( + { + "crispy_appended_text": self.appended_text, + "crispy_prepended_text": self.prepended_text, + "input_size": self.input_size, + "active": getattr(self, "active", False), + } + ) + if hasattr(self, "wrapper_class"): + extra_context["wrapper_class"] = self.wrapper_class template = self.get_template_name(template_pack) return render_field( - self.field, form, form_style, context, - template=template, attrs=self.attrs, - template_pack=template_pack, extra_context=extra_context, **kwargs + self.field, + form, + form_style, + context, + template=template, + attrs=self.attrs, + template_pack=template_pack, + extra_context=extra_context, + **kwargs, ) class AppendedText(PrependedAppendedText): def __init__(self, field, text, *args, **kwargs): - kwargs.pop('appended_text', None) - kwargs.pop('prepended_text', None) + kwargs.pop("appended_text", None) + kwargs.pop("prepended_text", None) self.text = text super().__init__(field, appended_text=text, **kwargs) class PrependedText(PrependedAppendedText): def __init__(self, field, text, *args, **kwargs): - kwargs.pop('appended_text', None) - kwargs.pop('prepended_text', None) + kwargs.pop("appended_text", None) + kwargs.pop("prepended_text", None) self.text = text super().__init__(field, prepended_text=text, **kwargs) @@ -72,22 +80,20 @@ class FormActions(LayoutObject): Submit('Save', 'Save', css_class='btn-primary') ) """ + template = "%s/layout/formactions.html" def __init__(self, *fields, **kwargs): self.fields = list(fields) - self.template = kwargs.pop('template', self.template) + self.template = kwargs.pop("template", self.template) self.attrs = kwargs - if 'css_class' in self.attrs: - self.attrs['class'] = self.attrs.pop('css_class') + if "css_class" in self.attrs: + self.attrs["class"] = self.attrs.pop("css_class") def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): html = self.get_rendered_fields(form, form_style, context, template_pack, **kwargs) template = self.get_template_name(template_pack) - context.update({ - 'formactions': self, - 'fields_output': html - }) + context.update({"formactions": self, "fields_output": html}) return render_to_string(template, context.flatten()) @@ -101,12 +107,12 @@ class InlineCheckboxes(Field): InlineCheckboxes('field_name') """ + template = "%s/layout/checkboxselectmultiple_inline.html" def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): return super().render( - form, form_style, context, template_pack=template_pack, - extra_context={'inline_class': 'inline'} + form, form_style, context, template_pack=template_pack, extra_context={"inline_class": "inline"} ) @@ -116,45 +122,56 @@ class InlineRadios(Field): InlineRadios('field_name') """ + template = "%s/layout/radioselect_inline.html" def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): return super().render( - form, form_style, context, template_pack=template_pack, - extra_context={'inline_class': 'inline'} + form, form_style, context, template_pack=template_pack, extra_context={"inline_class": "inline"} ) class FieldWithButtons(Div): - template = '%s/layout/field_with_buttons.html' - field_template = '%s/field.html' + template = "%s/layout/field_with_buttons.html" + field_template = "%s/field.html" def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, extra_context=None, **kwargs): # We first render the buttons field_template = self.field_template % template_pack - buttons = ''.join( + buttons = "".join( render_field( - field, form, form_style, context, - field_template, layout_object=self, - template_pack=template_pack, **kwargs - ) for field in self.fields[1:] + field, + form, + form_style, + context, + field_template, + layout_object=self, + template_pack=template_pack, + **kwargs, + ) + for field in self.fields[1:] ) - extra_context = {'div': self, 'buttons': buttons} + extra_context = {"div": self, "buttons": buttons} template = self.get_template_name(template_pack) if isinstance(self.fields[0], Field): # FieldWithButtons(Field('field_name'), StrictButton("go")) # We render the field passing its name and attributes return render_field( - self.fields[0][0], form, form_style, context, - template, attrs=self.fields[0].attrs, - template_pack=template_pack, extra_context=extra_context, **kwargs + self.fields[0][0], + form, + form_style, + context, + template, + attrs=self.fields[0].attrs, + template_pack=template_pack, + extra_context=extra_context, + **kwargs, ) else: return render_field( - self.fields[0], form, form_style, context, template, - extra_context=extra_context, **kwargs + self.fields[0], form, form_style, context, template, extra_context=extra_context, **kwargs ) @@ -164,28 +181,29 @@ class StrictButton(TemplateNameMixin): Button("button content", css_class="extra") """ - template = '%s/layout/button.html' - field_classes = 'btn' + + template = "%s/layout/button.html" + field_classes = "btn" def __init__(self, content, **kwargs): self.content = content - self.template = kwargs.pop('template', self.template) + self.template = kwargs.pop("template", self.template) - kwargs.setdefault('type', 'button') + kwargs.setdefault("type", "button") # We turn css_id and css_class into id and class - if 'css_id' in kwargs: - kwargs['id'] = kwargs.pop('css_id') - kwargs['class'] = self.field_classes - if 'css_class' in kwargs: - kwargs['class'] += " %s" % kwargs.pop('css_class') + if "css_id" in kwargs: + kwargs["id"] = kwargs.pop("css_id") + kwargs["class"] = self.field_classes + if "css_class" in kwargs: + kwargs["class"] += " %s" % kwargs.pop("css_class") self.flat_attrs = flatatt(kwargs) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): self.content = Template(str(self.content)).render(context) template = self.get_template_name(template_pack) - context.update({'button': self}) + context.update({"button": self}) return render_to_string(template, context.flatten()) @@ -194,11 +212,12 @@ class Container(Div): """ Base class used for `Tab` and `AccordionGroup`, represents a basic container concept """ + css_class = "" def __init__(self, name, *fields, **kwargs): super().__init__(*fields, **kwargs) - self.template = kwargs.pop('template', self.template) + self.template = kwargs.pop("template", self.template) self.name = name self._active_originally_included = "active" in kwargs self.active = kwargs.pop("active", False) @@ -213,10 +232,10 @@ def __contains__(self, field_name): def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): if self.active: - if 'active' not in self.css_class: - self.css_class += ' active' + if "active" not in self.css_class: + self.css_class += " active" else: - self.css_class = self.css_class.replace('active', '') + self.css_class = self.css_class.replace("active", "") return super().render(form, form_style, context, template_pack) @@ -224,6 +243,7 @@ class ContainerHolder(Div): """ Base class used for `TabHolder` and `Accordion`, groups containers """ + def first_container_with_errors(self, errors): """ Returns the first container with errors, otherwise returns None. @@ -244,7 +264,7 @@ def open_target_group_for_form(self, form): target = self.first_container_with_errors(form.errors.keys()) if target is None: target = self.fields[0] - if not getattr(target, '_active_originally_included', None): + if not getattr(target, "_active_originally_included", None): target.active = True return target @@ -259,8 +279,9 @@ class Tab(Container): Tab('tab_name', 'form_field_1', 'form_field_2', 'form_field_3') """ - css_class = 'tab-pane' - link_template = '%s/layout/tab-link.html' + + css_class = "tab-pane" + link_template = "%s/layout/tab-link.html" def render_link(self, template_pack=TEMPLATE_PACK, **kwargs): """ @@ -268,7 +289,7 @@ def render_link(self, template_pack=TEMPLATE_PACK, **kwargs): with active if needed. """ link_template = self.link_template % template_pack - return render_to_string(link_template, {'link': self}) + return render_to_string(link_template, {"link": self}) class TabHolder(ContainerHolder): @@ -280,7 +301,8 @@ class TabHolder(ContainerHolder): Tab('form_field_3') ) """ - template = '%s/layout/tab.html' + + template = "%s/layout/tab.html" def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): for tab in self.fields: @@ -289,13 +311,9 @@ def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwarg # Open the group that should be open. self.open_target_group_for_form(form) content = self.get_rendered_fields(form, form_style, context, template_pack) - links = ''.join(tab.render_link(template_pack) for tab in self.fields) + links = "".join(tab.render_link(template_pack) for tab in self.fields) - context.update({ - 'tabs': self, - 'links': links, - 'content': content - }) + context.update({"tabs": self, "links": links, "content": content}) template = self.get_template_name(template_pack) return render_to_string(template, context.flatten()) @@ -307,6 +325,7 @@ class AccordionGroup(Container): AccordionGroup("group name", "form_field_1", "form_field_2") """ + template = "%s/accordion-group.html" data_parent = "" # accordion parent div id. @@ -320,10 +339,11 @@ class Accordion(ContainerHolder): AccordionGroup("another group name", "form_field") ) """ + template = "%s/accordion.html" def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): - content = '' + content = "" # accordion group needs the parent div id to set `data-parent` (I don't # know why). This needs to be a unique id @@ -335,12 +355,10 @@ def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwarg for group in self.fields: group.data_parent = self.css_id - content += render_field( - group, form, form_style, context, template_pack=template_pack, **kwargs - ) + content += render_field(group, form, form_style, context, template_pack=template_pack, **kwargs) template = self.get_template_name(template_pack) - context.update({'accordion': self, 'content': content}) + context.update({"accordion": self, "content": content}) return render_to_string(template, context.flatten()) @@ -351,21 +369,22 @@ class Alert(Div): Alert(content='Warning! Best check yo self, you're not looking too good.') """ + template = "%s/layout/alert.html" css_class = "alert" def __init__(self, content, dismiss=True, block=False, **kwargs): fields = [] if block: - self.css_class += ' alert-block' + self.css_class += " alert-block" Div.__init__(self, *fields, **kwargs) - self.template = kwargs.pop('template', self.template) + self.template = kwargs.pop("template", self.template) self.content = content self.dismiss = dismiss def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): template = self.get_template_name(template_pack) - context.update({'alert': self, 'content': self.content, 'dismiss': self.dismiss}) + context.update({"alert": self, "content": self.content, "dismiss": self.dismiss}) return render_to_string(template, context.flatten()) @@ -378,10 +397,11 @@ class UneditableField(Field): UneditableField('field_name', css_class="input-xlarge") """ + template = "%s/layout/uneditable_input.html" def __init__(self, field, *args, **kwargs): - self.attrs = {'class': 'uneditable-input'} + self.attrs = {"class": "uneditable-input"} super().__init__(field, *args, **kwargs) diff --git a/crispy_forms/exceptions.py b/crispy_forms/exceptions.py index 61b2382ca..24df311e2 100644 --- a/crispy_forms/exceptions.py +++ b/crispy_forms/exceptions.py @@ -8,6 +8,7 @@ class FormHelpersException(CrispyError): We want to catch form helper errors as soon as possible because debugging templatetags is never fun. """ + pass diff --git a/crispy_forms/helper.py b/crispy_forms/helper.py index 720d1e648..726f1043d 100644 --- a/crispy_forms/helper.py +++ b/crispy_forms/helper.py @@ -31,8 +31,8 @@ def filter(self, *LayoutClasses, **kwargs): Returns a LayoutSlice pointing to layout objects of type `LayoutClass` """ self._check_layout() - max_level = kwargs.pop('max_level', 0) - greedy = kwargs.pop('greedy', False) + max_level = kwargs.pop("max_level", 0) + greedy = kwargs.pop("greedy", False) filtered_layout_objects = self.layout.get_layout_objects(LayoutClasses, max_level=max_level, greedy=greedy) return LayoutSlice(self.layout, filtered_layout_objects) @@ -184,13 +184,14 @@ def helper(self): {% load crispy_forms_tags %} {% crispy form %} """ - _form_method = 'post' - _form_action = '' - _form_style = 'default' + + _form_method = "post" + _form_action = "" + _form_style = "default" form = None - form_id = '' - form_class = '' - form_group_wrapper_class = '' + form_id = "" + form_class = "" + form_group_wrapper_class = "" layout = None form_tag = True form_error_title = None @@ -207,8 +208,8 @@ def helper(self): field_template = None disable_csrf = False use_custom_control = True - label_class = '' - field_class = '' + label_class = "" + field_class = "" include_media = True def __init__(self, form=None): @@ -228,9 +229,11 @@ def form_method(self): @form_method.setter def form_method(self, method): - if method.lower() not in ('get', 'post'): - raise FormHelpersException('Only GET and POST are valid in the \ - form_method helper attribute') + if method.lower() not in ("get", "post"): + raise FormHelpersException( + "Only GET and POST are valid in the \ + form_method helper attribute" + ) self._form_method = method.lower() @@ -248,16 +251,18 @@ def form_action(self, action): @property def form_style(self): if self._form_style == "default": - return '' + return "" if self._form_style == "inline": - return 'inlineLabels' + return "inlineLabels" @form_style.setter def form_style(self, style): - if style.lower() not in ('default', 'inline'): - raise FormHelpersException('Only default and inline are valid in the \ - form_style helper attribute') + if style.lower() not in ("default", "inline"): + raise FormHelpersException( + "Only default and inline are valid in the \ + form_style helper attribute" + ) self._form_style = style.lower() @@ -293,12 +298,7 @@ def render_layout(self, form, context, template_pack=TEMPLATE_PACK): form.crispy_field_template = self.field_template # This renders the specified Layout strictly - html = self.layout.render( - form, - self.form_style, - context, - template_pack=template_pack - ) + html = self.layout.render(form, self.form_style, context, template_pack=template_pack) # Rendering some extra fields if specified if self.render_unmentioned_fields or self.render_hidden_fields or self.render_required_fields: @@ -306,17 +306,11 @@ def render_layout(self, form, context, template_pack=TEMPLATE_PACK): left_fields_to_render = list_difference(fields, form.rendered_fields) for field in left_fields_to_render: if ( - self.render_unmentioned_fields or - (self.render_hidden_fields and form.fields[field].widget.is_hidden) or - (self.render_required_fields and form.fields[field].widget.is_required) + self.render_unmentioned_fields + or (self.render_hidden_fields and form.fields[field].widget.is_hidden) + or (self.render_required_fields and form.fields[field].widget.is_required) ): - html += render_field( - field, - form, - self.form_style, - context, - template_pack=template_pack - ) + html += render_field(field, form, self.form_style, context, template_pack=template_pack) return mark_safe(html) @@ -325,65 +319,68 @@ def get_attributes(self, template_pack=TEMPLATE_PACK): Used by crispy_forms_tags to get helper attributes """ items = { - 'disable_csrf': self.disable_csrf, - 'error_text_inline': self.error_text_inline, - 'field_class': self.field_class, - 'field_template': - self.field_template or '%s/field.html' % template_pack, - 'form_method': self.form_method.strip(), - 'form_show_errors': self.form_show_errors, - 'form_show_labels': self.form_show_labels, - 'form_style': self.form_style.strip(), - 'form_tag': self.form_tag, - 'help_text_inline': self.help_text_inline, - 'html5_required': self.html5_required, - 'include_media': self.include_media, - 'label_class': self.label_class, - 'use_custom_control': self.use_custom_control, + "disable_csrf": self.disable_csrf, + "error_text_inline": self.error_text_inline, + "field_class": self.field_class, + "field_template": self.field_template or "%s/field.html" % template_pack, + "form_method": self.form_method.strip(), + "form_show_errors": self.form_show_errors, + "form_show_labels": self.form_show_labels, + "form_style": self.form_style.strip(), + "form_tag": self.form_tag, + "help_text_inline": self.help_text_inline, + "html5_required": self.html5_required, + "include_media": self.include_media, + "label_class": self.label_class, + "use_custom_control": self.use_custom_control, } - if template_pack == 'bootstrap4': - if 'form-horizontal' in self.form_class.split(): - bootstrap_size_match = re.findall(r'col-(xl|lg|md|sm)-(\d+)', self.label_class) + if template_pack == "bootstrap4": + if "form-horizontal" in self.form_class.split(): + bootstrap_size_match = re.findall(r"col-(xl|lg|md|sm)-(\d+)", self.label_class) if bootstrap_size_match: - offset_pattern = 'offset-%s-%s' - items['bootstrap_checkbox_offsets'] = [offset_pattern % m for m in bootstrap_size_match] + offset_pattern = "offset-%s-%s" + items["bootstrap_checkbox_offsets"] = [offset_pattern % m for m in bootstrap_size_match] else: - bootstrap_size_match = re.findall(r'col-(lg|md|sm|xs)-(\d+)', self.label_class) + bootstrap_size_match = re.findall(r"col-(lg|md|sm|xs)-(\d+)", self.label_class) if bootstrap_size_match: - offset_pattern = 'col-%s-offset-%s' - items['bootstrap_checkbox_offsets'] = [offset_pattern % m for m in bootstrap_size_match] + offset_pattern = "col-%s-offset-%s" + items["bootstrap_checkbox_offsets"] = [offset_pattern % m for m in bootstrap_size_match] - items['attrs'] = {} + items["attrs"] = {} if self.attrs: - items['attrs'] = self.attrs.copy() + items["attrs"] = self.attrs.copy() if self.form_action: - items['attrs']['action'] = self.form_action.strip() + items["attrs"]["action"] = self.form_action.strip() if self.form_id: - items['attrs']['id'] = self.form_id.strip() + items["attrs"]["id"] = self.form_id.strip() if self.form_class: # uni_form TEMPLATE PACK has a uniForm class by default - if template_pack == 'uni_form': - items['attrs']['class'] = "uniForm %s" % self.form_class.strip() + if template_pack == "uni_form": + items["attrs"]["class"] = "uniForm %s" % self.form_class.strip() else: - items['attrs']['class'] = self.form_class.strip() + items["attrs"]["class"] = self.form_class.strip() else: - if template_pack == 'uni_form': - items['attrs']['class'] = self.attrs.get('class', '') + " uniForm" + if template_pack == "uni_form": + items["attrs"]["class"] = self.attrs.get("class", "") + " uniForm" if self.form_group_wrapper_class: - items['attrs']['form_group_wrapper_class'] = self.form_group_wrapper_class + items["attrs"]["form_group_wrapper_class"] = self.form_group_wrapper_class - items['flat_attrs'] = flatatt(items['attrs']) + items["flat_attrs"] = flatatt(items["attrs"]) if self.inputs: - items['inputs'] = self.inputs + items["inputs"] = self.inputs if self.form_error_title: - items['form_error_title'] = self.form_error_title.strip() + items["form_error_title"] = self.form_error_title.strip() if self.formset_error_title: - items['formset_error_title'] = self.formset_error_title.strip() + items["formset_error_title"] = self.formset_error_title.strip() for attribute_name, value in self.__dict__.items(): - if attribute_name not in items and attribute_name not in ['layout', 'inputs'] and not attribute_name.startswith('_'): + if ( + attribute_name not in items + and attribute_name not in ["layout", "inputs"] + and not attribute_name.startswith("_") + ): items[attribute_name] = value return items diff --git a/crispy_forms/layout.py b/crispy_forms/layout.py index b3c9dfe4e..5dfdb3519 100644 --- a/crispy_forms/layout.py +++ b/crispy_forms/layout.py @@ -6,9 +6,8 @@ class TemplateNameMixin: - def get_template_name(self, template_pack): - if '%s' in self.template: + if "%s" in self.template: template = self.template % template_pack else: template = self.template @@ -35,7 +34,7 @@ def __getattr__(self, name): having to declare them one by one """ # Check necessary for unpickling, see #107 - if 'fields' in self.__dict__ and hasattr(self.fields, name): + if "fields" in self.__dict__ and hasattr(self.fields, name): return getattr(self.fields, name) else: return object.__getattribute__(self, name) @@ -67,9 +66,9 @@ def get_layout_objects(self, *LayoutClasses, **kwargs): :param greedy: Boolean that indicates whether to be greedy. If set, max_level is skipped. """ - index = kwargs.pop('index', None) - max_level = kwargs.pop('max_level', 0) - greedy = kwargs.pop('greedy', False) + index = kwargs.pop("index", None) + max_level = kwargs.pop("max_level", 0) + greedy = kwargs.pop("greedy", False) pointers = [] @@ -87,14 +86,14 @@ def get_layout_objects(self, *LayoutClasses, **kwargs): # If it's a layout object and we haven't reached the max depth limit or greedy # we recursive call - if hasattr(layout_object, 'get_field_names') and (len(index) < max_level or greedy): - new_kwargs = {'index': index + [i], 'max_level': max_level, 'greedy': greedy} + if hasattr(layout_object, "get_field_names") and (len(index) < max_level or greedy): + new_kwargs = {"index": index + [i], "max_level": max_level, "greedy": greedy} pointers = pointers + layout_object.get_layout_objects(*LayoutClasses, **new_kwargs) return pointers def get_rendered_fields(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): - return ''.join( + return "".join( render_field(field, form, form_style, context, template_pack=template_pack, **kwargs) for field in self.fields ) @@ -128,6 +127,7 @@ class Layout(LayoutObject): ), ) """ + def __init__(self, *fields): self.fields = list(fields) @@ -149,19 +149,20 @@ class ButtonHolder(LayoutObject): Submit('Save', 'Save') ) """ + template = "%s/layout/buttonholder.html" def __init__(self, *fields, **kwargs): self.fields = list(fields) - self.css_class = kwargs.get('css_class', None) - self.css_id = kwargs.get('css_id', None) - self.template = kwargs.get('template', self.template) + self.css_class = kwargs.get("css_class", None) + self.css_id = kwargs.get("css_id", None) + self.template = kwargs.get("template", self.template) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): html = self.get_rendered_fields(form, form_style, context, template_pack, **kwargs) template = self.get_template_name(template_pack) - context.update({'buttonholder': self, 'fields_output': html}) + context.update({"buttonholder": self, "fields_output": html}) return render_to_string(template, context.flatten()) @@ -170,18 +171,19 @@ class BaseInput(TemplateNameMixin): """ A base class to reduce the amount of code in the Input classes. """ + template = "%s/layout/baseinput.html" def __init__(self, name, value, **kwargs): self.name = name self.value = value - self.id = kwargs.pop('css_id', '') + self.id = kwargs.pop("css_id", "") self.attrs = {} - if 'css_class' in kwargs: - self.field_classes += ' %s' % kwargs.pop('css_class') + if "css_class" in kwargs: + self.field_classes += " %s" % kwargs.pop("css_class") - self.template = kwargs.pop('template', self.template) + self.template = kwargs.pop("template", self.template) self.flat_attrs = flatatt(kwargs) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): @@ -191,7 +193,7 @@ def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwarg """ self.value = Template(str(self.value)).render(context) template = self.get_template_name(template_pack) - context.update({'input': self}) + context.update({"input": self}) return render_to_string(template, context.flatten()) @@ -204,10 +206,11 @@ class Submit(BaseInput): .. note:: The first argument is also slugified and turned into the id for the submit button. """ - input_type = 'submit' + + input_type = "submit" def __init__(self, *args, **kwargs): - self.field_classes = 'submit submitButton' if get_template_pack() == 'uni_form' else 'btn btn-primary' + self.field_classes = "submit submitButton" if get_template_pack() == "uni_form" else "btn btn-primary" super().__init__(*args, **kwargs) @@ -219,10 +222,11 @@ class Button(BaseInput): .. note:: The first argument is also slugified and turned into the id for the button. """ - input_type = 'button' + + input_type = "button" def __init__(self, *args, **kwargs): - self.field_classes = 'button' if get_template_pack() == 'uni_form' else 'btn' + self.field_classes = "button" if get_template_pack() == "uni_form" else "btn" super().__init__(*args, **kwargs) @@ -230,8 +234,9 @@ class Hidden(BaseInput): """ Used to create a Hidden input descriptor for the {% crispy %} template tag. """ - input_type = 'hidden' - field_classes = 'hidden' + + input_type = "hidden" + field_classes = "hidden" class Reset(BaseInput): @@ -242,10 +247,11 @@ class Reset(BaseInput): .. note:: The first argument is also slugified and turned into the id for the reset. """ - input_type = 'reset' + + input_type = "reset" def __init__(self, *args, **kwargs): - self.field_classes = 'reset resetButton' if get_template_pack() == 'uni_form' else 'btn btn-inverse' + self.field_classes = "reset resetButton" if get_template_pack() == "uni_form" else "btn btn-inverse" super().__init__(*args, **kwargs) @@ -268,64 +274,68 @@ class Fieldset(LayoutObject): 'form_field_2' ) """ + template = "%s/layout/fieldset.html" def __init__(self, legend, *fields, **kwargs): self.fields = list(fields) self.legend = legend - self.css_class = kwargs.pop('css_class', '') - self.css_id = kwargs.pop('css_id', None) - self.template = kwargs.pop('template', self.template) + self.css_class = kwargs.pop("css_class", "") + self.css_id = kwargs.pop("css_id", None) + self.template = kwargs.pop("template", self.template) self.flat_attrs = flatatt(kwargs) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): fields = self.get_rendered_fields(form, form_style, context, template_pack, **kwargs) - legend = '' + legend = "" if self.legend: - legend = '%s' % Template(str(self.legend)).render(context) + legend = "%s" % Template(str(self.legend)).render(context) template = self.get_template_name(template_pack) return render_to_string( - template, - {'fieldset': self, 'legend': legend, 'fields': fields, 'form_style': form_style} + template, {"fieldset": self, "legend": legend, "fields": fields, "form_style": form_style} ) class MultiField(LayoutObject): """ MultiField container. Renders to a MultiField
""" + template = "%s/layout/multifield.html" field_template = "%s/multifield.html" def __init__(self, label, *fields, **kwargs): self.fields = list(fields) self.label_html = label - self.label_class = kwargs.pop('label_class', 'blockLabel') - self.css_class = kwargs.pop('css_class', 'ctrlHolder') - self.css_id = kwargs.pop('css_id', None) - self.help_text = kwargs.pop('help_text', None) - self.template = kwargs.pop('template', self.template) - self.field_template = kwargs.pop('field_template', self.field_template) + self.label_class = kwargs.pop("label_class", "blockLabel") + self.css_class = kwargs.pop("css_class", "ctrlHolder") + self.css_id = kwargs.pop("css_id", None) + self.help_text = kwargs.pop("help_text", None) + self.template = kwargs.pop("template", self.template) + self.field_template = kwargs.pop("field_template", self.field_template) self.flat_attrs = flatatt(kwargs) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): # If a field within MultiField contains errors - if context['form_show_errors']: + if context["form_show_errors"]: for field in map(lambda pointer: pointer[1], self.get_field_names()): if field in form.errors: self.css_class += " error" field_template = self.field_template % template_pack fields_output = self.get_rendered_fields( - form, form_style, context, template_pack, template=field_template, - labelclass=self.label_class, layout_object=self, **kwargs + form, + form_style, + context, + template_pack, + template=field_template, + labelclass=self.label_class, + layout_object=self, + **kwargs, ) template = self.get_template_name(template_pack) - context.update({ - 'multifield': self, - 'fields_output': fields_output - }) + context.update({"multifield": self, "fields_output": fields_output}) return render_to_string(template, context.flatten()) @@ -338,25 +348,26 @@ class Div(LayoutObject): Div('form_field_1', 'form_field_2', css_id='div-example', css_class='divs') """ + template = "%s/layout/div.html" def __init__(self, *fields, **kwargs): self.fields = list(fields) - if hasattr(self, 'css_class') and 'css_class' in kwargs: - self.css_class += ' %s' % kwargs.pop('css_class') - if not hasattr(self, 'css_class'): - self.css_class = kwargs.pop('css_class', None) + if hasattr(self, "css_class") and "css_class" in kwargs: + self.css_class += " %s" % kwargs.pop("css_class") + if not hasattr(self, "css_class"): + self.css_class = kwargs.pop("css_class", None) - self.css_id = kwargs.pop('css_id', '') - self.template = kwargs.pop('template', self.template) + self.css_id = kwargs.pop("css_id", "") + self.template = kwargs.pop("template", self.template) self.flat_attrs = flatatt(kwargs) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): fields = self.get_rendered_fields(form, form_style, context, template_pack, **kwargs) template = self.get_template_name(template_pack) - return render_to_string(template, {'div': self, 'fields': fields}) + return render_to_string(template, {"div": self, "fields": fields}) class Row(Div): @@ -365,6 +376,7 @@ class Row(Div): Row('form_field_1', 'form_field_2', 'form_field_3') """ + template = "%s/layout/row.html" @@ -379,6 +391,7 @@ class Column(Div): Column('form_field_1', 'form_field_2', css_class='col-xs-6',) """ + template = "%s/layout/column.html" @@ -409,41 +422,47 @@ class Field(LayoutObject): Field('field_name', style="color: #333;", css_class="whatever", id="field_name") """ + template = "%s/field.html" def __init__(self, *args, **kwargs): self.fields = list(args) - if not hasattr(self, 'attrs'): + if not hasattr(self, "attrs"): self.attrs = {} else: # Make sure shared state is not edited. self.attrs = self.attrs.copy() - if 'css_class' in kwargs: - if 'class' in self.attrs: - self.attrs['class'] += " %s" % kwargs.pop('css_class') + if "css_class" in kwargs: + if "class" in self.attrs: + self.attrs["class"] += " %s" % kwargs.pop("css_class") else: - self.attrs['class'] = kwargs.pop('css_class') + self.attrs["class"] = kwargs.pop("css_class") - self.wrapper_class = kwargs.pop('wrapper_class', None) - self.template = kwargs.pop('template', self.template) + self.wrapper_class = kwargs.pop("wrapper_class", None) + self.template = kwargs.pop("template", self.template) # We use kwargs as HTML attributes, turning data_id='test' into data-id='test' - self.attrs.update({k.replace('_', '-'): conditional_escape(v) for k, v in kwargs.items()}) + self.attrs.update({k.replace("_", "-"): conditional_escape(v) for k, v in kwargs.items()}) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, extra_context=None, **kwargs): if extra_context is None: extra_context = {} - if hasattr(self, 'wrapper_class'): - extra_context['wrapper_class'] = self.wrapper_class + if hasattr(self, "wrapper_class"): + extra_context["wrapper_class"] = self.wrapper_class template = self.get_template_name(template_pack) return self.get_rendered_fields( - form, form_style, context, template_pack, - template=template, attrs=self.attrs, extra_context=extra_context, - **kwargs + form, + form_style, + context, + template_pack, + template=template, + attrs=self.attrs, + extra_context=extra_context, + **kwargs, ) @@ -464,8 +483,9 @@ class MultiWidgetField(Field): .. note:: To override widget's css class use ``class`` not ``css_class``. """ + def __init__(self, *args, **kwargs): self.fields = list(args) - self.attrs = kwargs.pop('attrs', {}) - self.template = kwargs.pop('template', self.template) - self.wrapper_class = kwargs.pop('wrapper_class', None) + self.attrs = kwargs.pop("attrs", {}) + self.template = kwargs.pop("template", self.template) + self.wrapper_class = kwargs.pop("wrapper_class", None) diff --git a/crispy_forms/layout_slice.py b/crispy_forms/layout_slice.py index 38539142e..43d761d92 100644 --- a/crispy_forms/layout_slice.py +++ b/crispy_forms/layout_slice.py @@ -64,18 +64,19 @@ def pre_map(self, function): except IndexError: # We could avoid this exception, recalculating pointers. # However this case is most of the time an undesired behavior - raise DynamicError("Trying to wrap a field within an already wrapped field, \ - recheck your filter or layout") + raise DynamicError( + "Trying to wrap a field within an already wrapped field, \ + recheck your filter or layout" + ) def wrap(self, LayoutClass, *args, **kwargs): """ Wraps every layout object pointed in `self.slice` under a `LayoutClass` instance with `args` and `kwargs` passed. """ + def wrap_object(layout_object, j): - layout_object.fields[j] = self.wrapped_object( - LayoutClass, layout_object.fields[j], *args, **kwargs - ) + layout_object.fields[j] = self.wrapped_object(LayoutClass, layout_object.fields[j], *args, **kwargs) self.pre_map(wrap_object) @@ -85,11 +86,10 @@ def wrap_once(self, LayoutClass, *args, **kwargs): `args` and `kwargs` passed, unless layout object's parent is already a subclass of `LayoutClass`. """ + def wrap_object_once(layout_object, j): if not isinstance(layout_object, LayoutClass): - layout_object.fields[j] = self.wrapped_object( - LayoutClass, layout_object.fields[j], *args, **kwargs - ) + layout_object.fields[j] = self.wrapped_object(LayoutClass, layout_object.fields[j], *args, **kwargs) self.pre_map(wrap_object_once) @@ -133,10 +133,7 @@ def map(self, function): layout_object = layout_object.fields[i] # If update_attrs is applied to a string, we call to its wrapping layout object - if ( - function.__name__ == 'update_attrs' - and isinstance(layout_object, str) - ): + if function.__name__ == "update_attrs" and isinstance(layout_object, str): function(previous_layout_object) else: function(layout_object) @@ -145,14 +142,15 @@ def update_attributes(self, **original_kwargs): """ Updates attributes of every layout object pointed in `self.slice` using kwargs """ + def update_attrs(layout_object): kwargs = original_kwargs.copy() - if hasattr(layout_object, 'attrs'): - if 'css_class' in kwargs: - if 'class' in layout_object.attrs: - layout_object.attrs['class'] += " %s" % kwargs.pop('css_class') + if hasattr(layout_object, "attrs"): + if "css_class" in kwargs: + if "class" in layout_object.attrs: + layout_object.attrs["class"] += " %s" % kwargs.pop("css_class") else: - layout_object.attrs['class'] = kwargs.pop('css_class') + layout_object.attrs["class"] = kwargs.pop("css_class") layout_object.attrs.update(kwargs) self.map(update_attrs) diff --git a/crispy_forms/templatetags/crispy_forms_field.py b/crispy_forms/templatetags/crispy_forms_field.py index 73bf54c55..c36161722 100644 --- a/crispy_forms/templatetags/crispy_forms_field.py +++ b/crispy_forms/templatetags/crispy_forms_field.py @@ -52,7 +52,7 @@ def classes(field): """ Returns CSS classes of a field """ - return field.widget.attrs.get('class', None) + return field.widget.attrs.get("class", None) @register.filter @@ -73,7 +73,7 @@ class CrispyFieldNode(template.Node): def __init__(self, field, attrs): self.field = field self.attrs = attrs - self.html5_required = 'html5_required' + self.html5_required = "html5_required" def render(self, context): # Nodes are not threadsafe so we must store and look up our instance @@ -82,7 +82,7 @@ def render(self, context): context.render_context[self] = ( template.Variable(self.field), self.attrs, - template.Variable(self.html5_required) + template.Variable(self.html5_required), ) field, attrs, html5_required = context.render_context[self] @@ -93,26 +93,26 @@ def render(self, context): html5_required = False # If template pack has been overridden in FormHelper we can pick it from context - template_pack = context.get('template_pack', TEMPLATE_PACK) + template_pack = context.get("template_pack", TEMPLATE_PACK) # There are special django widgets that wrap actual widgets, # such as forms.widgets.MultiWidget, admin.widgets.RelatedFieldWidgetWrapper - widgets = getattr(field.field.widget, 'widgets', [getattr(field.field.widget, 'widget', field.field.widget)]) + widgets = getattr(field.field.widget, "widgets", [getattr(field.field.widget, "widget", field.field.widget)]) if isinstance(attrs, dict): attrs = [attrs] * len(widgets) converters = { - 'textinput': 'textinput textInput', - 'fileinput': 'fileinput fileUpload', - 'passwordinput': 'textinput textInput', + "textinput": "textinput textInput", + "fileinput": "fileinput fileUpload", + "passwordinput": "textinput textInput", } - converters.update(getattr(settings, 'CRISPY_CLASS_CONVERTERS', {})) + converters.update(getattr(settings, "CRISPY_CLASS_CONVERTERS", {})) for widget, attr in zip(widgets, attrs): class_name = widget.__class__.__name__.lower() class_name = converters.get(class_name, class_name) - css_class = widget.attrs.get('class', '') + css_class = widget.attrs.get("class", "") if css_class: if css_class.find(class_name) == -1: css_class += " %s" % class_name @@ -120,32 +120,29 @@ def render(self, context): css_class = class_name if ( - template_pack == 'bootstrap3' + template_pack == "bootstrap3" and not is_checkbox(field) and not is_file(field) and not is_multivalue(field) ): - css_class += ' form-control' + css_class += " form-control" if field.errors: - css_class += ' form-control-danger' + css_class += " form-control-danger" - if ( - template_pack == 'bootstrap4' - and not is_multivalue(field) - ): + if template_pack == "bootstrap4" and not is_multivalue(field): if not is_checkbox(field): - css_class += ' form-control' + css_class += " form-control" if is_file(field): - css_class += '-file' + css_class += "-file" if field.errors: - css_class += ' is-invalid' + css_class += " is-invalid" - widget.attrs['class'] = css_class + widget.attrs["class"] = css_class # HTML5 required attribute - if html5_required and field.field.required and 'required' not in widget.attrs: - if field.field.widget.__class__.__name__ != 'RadioSelect': - widget.attrs['required'] = 'required' + if html5_required and field.field.required and "required" not in widget.attrs: + if field.field.widget.__class__.__name__ != "RadioSelect": + widget.attrs["required"] = "required" for attribute_name, attribute in attr.items(): attribute_name = template.Variable(attribute_name).resolve(context) @@ -188,14 +185,10 @@ def crispy_addon(field, append="", prepend="", form_show_labels=True): {% crispy_addon form.my_field append=".00" %} """ if field: - context = Context({ - 'field': field, - 'form_show_errors': True, - 'form_show_labels': form_show_labels, - }) - template = loader.get_template('%s/layout/prepended_appended_text.html' % get_template_pack()) - context['crispy_prepended_text'] = prepend - context['crispy_appended_text'] = append + context = Context({"field": field, "form_show_errors": True, "form_show_labels": form_show_labels,}) + template = loader.get_template("%s/layout/prepended_appended_text.html" % get_template_pack()) + context["crispy_prepended_text"] = prepend + context["crispy_appended_text"] = append if not prepend and not append: raise TypeError("Expected a prepend and/or append argument") diff --git a/crispy_forms/templatetags/crispy_forms_filters.py b/crispy_forms/templatetags/crispy_forms_filters.py index dccb0eb17..e99e7d04b 100644 --- a/crispy_forms/templatetags/crispy_forms_filters.py +++ b/crispy_forms/templatetags/crispy_forms_filters.py @@ -13,18 +13,18 @@ @lru_cache() def uni_formset_template(template_pack=TEMPLATE_PACK): - return get_template('%s/uni_formset.html' % template_pack) + return get_template("%s/uni_formset.html" % template_pack) @lru_cache() def uni_form_template(template_pack=TEMPLATE_PACK): - return get_template('%s/uni_form.html' % template_pack) + return get_template("%s/uni_form.html" % template_pack) register = template.Library() -@register.filter(name='crispy') +@register.filter(name="crispy") def as_crispy_form(form, template_pack=TEMPLATE_PACK, label_class="", field_class=""): """ The original and still very useful way to generate a div elegant form/formset:: @@ -44,24 +44,26 @@ def as_crispy_form(form, template_pack=TEMPLATE_PACK, label_class="", field_clas {{ myform|label_class:"col-lg-2",field_class:"col-lg-8" }} """ - c = Context({ - 'field_class': field_class, - 'field_template': '%s/field.html' % template_pack, - 'form_show_errors': True, - 'form_show_labels': True, - 'label_class': label_class, - }).flatten() + c = Context( + { + "field_class": field_class, + "field_template": "%s/field.html" % template_pack, + "form_show_errors": True, + "form_show_labels": True, + "label_class": label_class, + } + ).flatten() if isinstance(form, BaseFormSet): template = uni_formset_template(template_pack) - c['formset'] = form + c["formset"] = form else: template = uni_form_template(template_pack) - c['form'] = form + c["form"] = form return template.render(c) -@register.filter(name='as_crispy_errors') +@register.filter(name="as_crispy_errors") def as_crispy_errors(form, template_pack=TEMPLATE_PACK): """ Renders only form errors the same way as django-crispy-forms:: @@ -74,16 +76,16 @@ def as_crispy_errors(form, template_pack=TEMPLATE_PACK): {{ form|as_crispy_errors:"bootstrap" }} """ if isinstance(form, BaseFormSet): - template = get_template('%s/errors_formset.html' % template_pack) - c = Context({'formset': form}).flatten() + template = get_template("%s/errors_formset.html" % template_pack) + c = Context({"formset": form}).flatten() else: - template = get_template('%s/errors.html' % template_pack) - c = Context({'form': form}).flatten() + template = get_template("%s/errors.html" % template_pack) + c = Context({"form": form}).flatten() return template.render(c) -@register.filter(name='as_crispy_field') +@register.filter(name="as_crispy_field") def as_crispy_field(field, template_pack=TEMPLATE_PACK, label_class="", field_class=""): """ Renders a form field like a django-crispy-forms field:: @@ -96,29 +98,29 @@ def as_crispy_field(field, template_pack=TEMPLATE_PACK, label_class="", field_cl {{ form.field|as_crispy_field:"bootstrap" }} """ if not isinstance(field, boundfield.BoundField) and settings.DEBUG: - raise CrispyError('|as_crispy_field got passed an invalid or inexistent field') + raise CrispyError("|as_crispy_field got passed an invalid or inexistent field") attributes = { - 'field': field, - 'form_show_errors': True, - 'form_show_labels': True, - 'label_class': label_class, - 'field_class': field_class, + "field": field, + "form_show_errors": True, + "form_show_labels": True, + "label_class": label_class, + "field_class": field_class, } - helper = getattr(field.form, 'helper', None) + helper = getattr(field.form, "helper", None) template_path = None if helper is not None: attributes.update(helper.get_attributes(template_pack)) template_path = helper.field_template if not template_path: - template_path = '%s/field.html' % template_pack + template_path = "%s/field.html" % template_pack template = get_template(template_path) c = Context(attributes).flatten() return template.render(c) -@register.filter(name='flatatt') +@register.filter(name="flatatt") def flatatt_filter(attrs): return mark_safe(flatatt(attrs)) diff --git a/crispy_forms/templatetags/crispy_forms_tags.py b/crispy_forms/templatetags/crispy_forms_tags.py index dc0e3db2a..60f523364 100644 --- a/crispy_forms/templatetags/crispy_forms_tags.py +++ b/crispy_forms/templatetags/crispy_forms_tags.py @@ -25,6 +25,7 @@ class ForLoopSimulator: Fieldset("Item {{ forloop.counter }}", [...]) HTML("{% if forloop.first %}First form text{% endif %}" """ + def __init__(self, formset): self.len_values = len(formset.forms) @@ -36,7 +37,7 @@ def __init__(self, formset): self.revcounter0 = self.len_values - 1 # Boolean values designating first and last times through loop. self.first = True - self.last = (0 == self.len_values - 1) + self.last = 0 == self.len_values - 1 def iterate(self): """ @@ -47,7 +48,7 @@ def iterate(self): self.revcounter -= 1 self.revcounter0 -= 1 self.first = False - self.last = (self.revcounter0 == self.len_values - 1) + self.last = self.revcounter0 == self.len_values - 1 class BasicNode(template.Node): @@ -58,6 +59,7 @@ class BasicNode(template.Node): both the form object and parses out the helper string into attributes that templates can easily handle. """ + def __init__(self, form, helper, template_pack=None): self.form = form if helper is not None: @@ -82,7 +84,7 @@ def get_render(self, context): if self not in context.render_context: context.render_context[self] = ( template.Variable(self.form), - template.Variable(self.helper) if self.helper else None + template.Variable(self.helper) if self.helper else None, ) form, helper = context.render_context[self] @@ -92,7 +94,7 @@ def get_render(self, context): else: # If the user names the helper within the form `helper` (standard), we use it # This allows us to have simplified tag syntax: {% crispy form %} - helper = FormHelper() if not hasattr(actual_form, 'helper') else actual_form.helper + helper = FormHelper() if not hasattr(actual_form, "helper") else actual_form.helper # use template_pack from helper, if defined try: @@ -107,27 +109,29 @@ def get_render(self, context): is_formset = isinstance(actual_form, BaseFormSet) response_dict = self.get_response_dict(helper, context, is_formset) node_context = context.__copy__() - node_context.update({'is_bound': actual_form.is_bound}) + node_context.update({"is_bound": actual_form.is_bound}) node_context.update(response_dict) final_context = node_context.__copy__() # If we have a helper's layout we use it, for the form or the formset's forms if helper and helper.layout: if not is_formset: - actual_form.form_html = helper.render_layout(actual_form, node_context, template_pack=self.template_pack) + actual_form.form_html = helper.render_layout( + actual_form, node_context, template_pack=self.template_pack + ) else: forloop = ForLoopSimulator(actual_form) helper.render_hidden_fields = True for form in actual_form: - node_context.update({'forloop': forloop}) - node_context.update({'formset_form': form}) + node_context.update({"forloop": forloop}) + node_context.update({"formset_form": form}) form.form_html = helper.render_layout(form, node_context, template_pack=self.template_pack) forloop.iterate() if is_formset: - final_context['formset'] = actual_form + final_context["formset"] = actual_form else: - final_context['form'] = actual_form + final_context["form"] = actual_form return final_context @@ -139,7 +143,7 @@ def get_response_dict(self, helper, context, is_formset): :param is_formset: Boolean value. If set to True, indicates we are working with a formset. """ if not isinstance(helper, FormHelper): - raise TypeError('helper object provided to {% crispy %} tag must be a crispy.helper.FormHelper object.') + raise TypeError("helper object provided to {% crispy %} tag must be a crispy.helper.FormHelper object.") attrs = helper.get_attributes(template_pack=self.template_pack) form_type = "form" @@ -148,29 +152,29 @@ def get_response_dict(self, helper, context, is_formset): # We take form/formset parameters from attrs if they are set, otherwise we use defaults response_dict = { - '%s_action' % form_type: attrs['attrs'].get('action', ''), - '%s_attrs' % form_type: attrs.get('attrs', ''), - '%s_class' % form_type: attrs['attrs'].get('class', ''), - '%s_id' % form_type: attrs['attrs'].get('id', ''), - '%s_method' % form_type: attrs.get('form_method', 'post'), - '%s_style' % form_type: attrs.get('form_style', None), - '%s_tag' % form_type: attrs.get('form_tag', True), - 'disable_csrf': attrs.get('disable_csrf', False), - 'error_text_inline': attrs.get('error_text_inline', True), - 'field_class': attrs.get('field_class', ''), - 'field_template': attrs.get('field_template', ''), - 'flat_attrs': attrs.get('flat_attrs', ''), - 'form_error_title': attrs.get('form_error_title', None), - 'form_show_errors': attrs.get('form_show_errors', True), - 'form_show_labels': attrs.get('form_show_labels', True), - 'formset_error_title': attrs.get('formset_error_title', None), - 'help_text_inline': attrs.get('help_text_inline', False), - 'html5_required': attrs.get('html5_required', False), - 'include_media': attrs.get('include_media', True), - 'inputs': attrs.get('inputs', []), - 'is_formset': is_formset, - 'label_class': attrs.get('label_class', ''), - 'template_pack': self.template_pack, + "%s_action" % form_type: attrs["attrs"].get("action", ""), + "%s_attrs" % form_type: attrs.get("attrs", ""), + "%s_class" % form_type: attrs["attrs"].get("class", ""), + "%s_id" % form_type: attrs["attrs"].get("id", ""), + "%s_method" % form_type: attrs.get("form_method", "post"), + "%s_style" % form_type: attrs.get("form_style", None), + "%s_tag" % form_type: attrs.get("form_tag", True), + "disable_csrf": attrs.get("disable_csrf", False), + "error_text_inline": attrs.get("error_text_inline", True), + "field_class": attrs.get("field_class", ""), + "field_template": attrs.get("field_template", ""), + "flat_attrs": attrs.get("flat_attrs", ""), + "form_error_title": attrs.get("form_error_title", None), + "form_show_errors": attrs.get("form_show_errors", True), + "form_show_labels": attrs.get("form_show_labels", True), + "formset_error_title": attrs.get("formset_error_title", None), + "help_text_inline": attrs.get("help_text_inline", False), + "html5_required": attrs.get("html5_required", False), + "include_media": attrs.get("include_media", True), + "inputs": attrs.get("inputs", []), + "is_formset": is_formset, + "label_class": attrs.get("label_class", ""), + "template_pack": self.template_pack, } # Handles custom attributes added to helpers @@ -178,30 +182,30 @@ def get_response_dict(self, helper, context, is_formset): if attribute_name not in response_dict: response_dict[attribute_name] = value - if 'csrf_token' in context: - response_dict['csrf_token'] = context['csrf_token'] + if "csrf_token" in context: + response_dict["csrf_token"] = context["csrf_token"] return response_dict @lru_cache() def whole_uni_formset_template(template_pack=TEMPLATE_PACK): - return get_template('%s/whole_uni_formset.html' % template_pack) + return get_template("%s/whole_uni_formset.html" % template_pack) @lru_cache() def whole_uni_form_template(template_pack=TEMPLATE_PACK): - return get_template('%s/whole_uni_form.html' % template_pack) + return get_template("%s/whole_uni_form.html" % template_pack) class CrispyFormNode(BasicNode): def render(self, context): c = self.get_render(context).flatten() - if self.actual_helper is not None and getattr(self.actual_helper, 'template', False): + if self.actual_helper is not None and getattr(self.actual_helper, "template", False): template = get_template(self.actual_helper.template) else: - if c['is_formset']: + if c["is_formset"]: template = whole_uni_formset_template(self.template_pack) else: template = whole_uni_form_template(self.template_pack) @@ -250,25 +254,18 @@ def do_uni_form(parser, token): pass # {% crispy form 'bootstrap' %} - if ( - helper is not None and - isinstance(helper, str) and - ("'" in helper or '"' in helper) - ): + if helper is not None and isinstance(helper, str) and ("'" in helper or '"' in helper): template_pack = helper helper = None if template_pack is not None: template_pack = template_pack[1:-1] ALLOWED_TEMPLATE_PACKS = getattr( - settings, - 'CRISPY_ALLOWED_TEMPLATE_PACKS', - ('bootstrap', 'uni_form', 'bootstrap3', 'bootstrap4') + settings, "CRISPY_ALLOWED_TEMPLATE_PACKS", ("bootstrap", "uni_form", "bootstrap3", "bootstrap4") ) if template_pack not in ALLOWED_TEMPLATE_PACKS: raise template.TemplateSyntaxError( - "crispy tag's template_pack argument should be in %s" % - str(ALLOWED_TEMPLATE_PACKS) + "crispy tag's template_pack argument should be in %s" % str(ALLOWED_TEMPLATE_PACKS) ) return CrispyFormNode(form, helper, template_pack=template_pack) diff --git a/crispy_forms/templatetags/crispy_forms_utils.py b/crispy_forms/templatetags/crispy_forms_utils.py index 0ea7f1dd6..2b206a323 100644 --- a/crispy_forms/templatetags/crispy_forms_utils.py +++ b/crispy_forms/templatetags/crispy_forms_utils.py @@ -9,8 +9,8 @@ @keep_lazy(str) def remove_spaces(value): - html = re.sub(r'>\s{3,}<', '> <', force_str(value)) - return re.sub(r'/><', r'/> <', force_str(html)) + html = re.sub(r">\s{3,}<", "> <", force_str(value)) + return re.sub(r"/><", r"/> <", force_str(html)) class SpecialSpacelessNode(template.Node): @@ -28,7 +28,7 @@ def specialspaceless(parser, token): after buttons an inputs, necessary for Bootstrap to place them correctly in the layout. """ - nodelist = parser.parse(('endspecialspaceless',)) + nodelist = parser.parse(("endspecialspaceless",)) parser.delete_first_token() return SpecialSpacelessNode(nodelist) diff --git a/crispy_forms/tests/conftest.py b/crispy_forms/tests/conftest.py index 594df5568..cdd8e7a1e 100644 --- a/crispy_forms/tests/conftest.py +++ b/crispy_forms/tests/conftest.py @@ -2,41 +2,34 @@ from crispy_forms.layout import HTML, Div, Field, Fieldset, Layout, Submit -only_uni_form = pytest.mark.only('uni_form') -only_bootstrap = pytest.mark.only('bootstrap', 'bootstrap3', 'bootstrap4') -only_bootstrap3 = pytest.mark.only('bootstrap3') -only_bootstrap4 = pytest.mark.only('bootstrap4') +only_uni_form = pytest.mark.only("uni_form") +only_bootstrap = pytest.mark.only("bootstrap", "bootstrap3", "bootstrap4") +only_bootstrap3 = pytest.mark.only("bootstrap3") +only_bootstrap4 = pytest.mark.only("bootstrap4") @pytest.fixture def advanced_layout(): return Layout( Div( - Div(Div('email')), - Div(Field('password1')), + Div(Div("email")), + Div(Field("password1")), Submit("save", "save"), - Fieldset( - "legend", - 'first_name', - HTML("extra text"), - ), - Layout( - "password2", - ), + Fieldset("legend", "first_name", HTML("extra text"),), + Layout("password2",), ), - 'last_name', + "last_name", ) -@pytest.fixture(autouse=True, params=('uni_form', 'bootstrap', 'bootstrap3', - 'bootstrap4')) +@pytest.fixture(autouse=True, params=("uni_form", "bootstrap", "bootstrap3", "bootstrap4")) def template_packs(request, settings): check_template_pack(request.node, request.param) settings.CRISPY_TEMPLATE_PACK = request.param def check_template_pack(node, template_pack): - mark = node.get_closest_marker('only') + mark = node.get_closest_marker("only") if mark: if template_pack not in mark.args: - pytest.skip('Requires %s template pack' % ' or '.join(mark.args)) + pytest.skip("Requires %s template pack" % " or ".join(mark.args)) diff --git a/crispy_forms/tests/forms.py b/crispy_forms/tests/forms.py index 4a2ac1779..a1410416f 100644 --- a/crispy_forms/tests/forms.py +++ b/crispy_forms/tests/forms.py @@ -6,7 +6,9 @@ class SampleForm(forms.Form): is_company = forms.CharField(label="company", required=False, widget=forms.CheckboxInput()) - email = forms.EmailField(label="email", max_length=30, required=True, widget=forms.TextInput(), help_text="Insert your email") + email = forms.EmailField( + label="email", max_length=30, required=True, widget=forms.TextInput(), help_text="Insert your email" + ) password1 = forms.CharField(label="password", max_length=30, required=True, widget=forms.PasswordInput()) password2 = forms.CharField(label="re-enter password", max_length=30, required=True, widget=forms.PasswordInput()) first_name = forms.CharField(label="first name", max_length=5, required=True, widget=forms.TextInput()) @@ -15,8 +17,8 @@ class SampleForm(forms.Form): def clean(self): super().clean() - password1 = self.cleaned_data.get('password1', None) - password2 = self.cleaned_data.get('password2', None) + password1 = self.cleaned_data.get("password1", None) + password2 = self.cleaned_data.get("password2", None) if not password1 and not password2 or password1 != password2: raise forms.ValidationError("Passwords dont match") @@ -31,42 +33,27 @@ def __init__(self, *args, **kwargs): class CheckboxesSampleForm(forms.Form): checkboxes = forms.MultipleChoiceField( - choices=( - (1, "Option one"), - (2, "Option two"), - (3, "Option three") - ), + choices=((1, "Option one"), (2, "Option two"), (3, "Option three")), initial=(1,), widget=forms.CheckboxSelectMultiple, ) alphacheckboxes = forms.MultipleChoiceField( - choices=( - ('option_one', "Option one"), - ('option_two', "Option two"), - ('option_three', "Option three") - ), - initial=('option_two', 'option_three'), + choices=(("option_one", "Option one"), ("option_two", "Option two"), ("option_three", "Option three")), + initial=("option_two", "option_three"), widget=forms.CheckboxSelectMultiple, ) numeric_multiple_checkboxes = forms.MultipleChoiceField( - choices=( - (1, "Option one"), - (2, "Option two"), - (3, "Option three") - ), + choices=((1, "Option one"), (2, "Option two"), (3, "Option three")), initial=(1, 2), widget=forms.CheckboxSelectMultiple, ) inline_radios = forms.ChoiceField( - choices=( - ('option_one', "Option one"), - ('option_two', "Option two"), - ), + choices=(("option_one", "Option one"), ("option_two", "Option two"),), widget=forms.RadioSelect, - initial='option_two', + initial="option_two", ) @@ -78,8 +65,8 @@ class CrispyTestModel(models.Model): class SampleForm3(forms.ModelForm): class Meta: model = CrispyTestModel - fields = ['email', 'password'] - exclude = ['password'] + fields = ["email", "password"] + exclude = ["password"] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -94,8 +81,9 @@ class Meta: django.core.exceptions.FieldError: Unknown field(s) (a, l, _) specified for CrispyTestModel because obviously it casts the string to a set """ + model = CrispyTestModel - fields = '__all__' # eliminate RemovedInDjango18Warning + fields = "__all__" # eliminate RemovedInDjango18Warning class SampleForm5(forms.Form): @@ -104,21 +92,15 @@ class SampleForm5(forms.Form): (2, 2), (1000, 1000), ] - checkbox_select_multiple = forms.MultipleChoiceField( - widget=forms.CheckboxSelectMultiple, - choices=choices - ) - radio_select = forms.ChoiceField( - widget=forms.RadioSelect, - choices=choices - ) + checkbox_select_multiple = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=choices) + radio_select = forms.ChoiceField(widget=forms.RadioSelect, choices=choices) pk = forms.IntegerField() class SampleFormWithMedia(forms.Form): class Media: - css = {'all': ('test.css',)} - js = ('test.js',) + css = {"all": ("test.css",)} + js = ("test.js",) class SampleFormWithMultiValueField(forms.Form): @@ -126,13 +108,7 @@ class SampleFormWithMultiValueField(forms.Form): class CrispyEmptyChoiceTestModel(models.Model): - fruit = models.CharField( - choices=[ - ('apple', 'Apple'), - ('pear', 'Pear')], - null=True, - blank=True, - ) + fruit = models.CharField(choices=[("apple", "Apple"), ("pear", "Pear")], null=True, blank=True,) class SampleForm6(forms.ModelForm): @@ -146,9 +122,10 @@ class Meta: with initial value None, this choice should be selected. """ + model = CrispyEmptyChoiceTestModel - fields = ['fruit'] - widgets = {'fruit': forms.RadioSelect()} + fields = ["fruit"] + widgets = {"fruit": forms.RadioSelect()} class SampleForm7(forms.ModelForm): @@ -157,7 +134,7 @@ class SampleForm7(forms.ModelForm): class Meta: model = CrispyTestModel - fields = ('email', 'password', 'password2') + fields = ("email", "password", "password2") class SampleForm8(forms.ModelForm): @@ -166,4 +143,4 @@ class SampleForm8(forms.ModelForm): class Meta: model = CrispyTestModel - fields = ('email', 'password2', 'password') + fields = ("email", "password2", "password") diff --git a/crispy_forms/tests/test_dynamic_api.py b/crispy_forms/tests/test_dynamic_api.py index 246d150a3..6c7f31c24 100644 --- a/crispy_forms/tests/test_dynamic_api.py +++ b/crispy_forms/tests/test_dynamic_api.py @@ -13,30 +13,22 @@ def test_wrap_all_fields(): helper = FormHelper() - layout = Layout( - 'email', - 'password1', - 'password2', - ) + layout = Layout("email", "password1", "password2",) helper.layout = layout helper.all().wrap(Field, css_class="test-class") for field in layout.fields: assert isinstance(field, Field) - assert field.attrs['class'] == "test-class" + assert field.attrs["class"] == "test-class" - assert layout[0][0] == 'email' - assert layout[1][0] == 'password1' - assert layout[2][0] == 'password2' + assert layout[0][0] == "email" + assert layout[1][0] == "password1" + assert layout[2][0] == "password2" def test_wrap_selected_fields(): helper = FormHelper() - layout = Layout( - 'email', - 'password1', - 'password2', - ) + layout = Layout("email", "password1", "password2",) helper.layout = layout helper[1:3].wrap(Field, css_class="test-class") @@ -44,131 +36,91 @@ def test_wrap_selected_fields(): assert isinstance(layout.fields[1], Field) assert isinstance(layout.fields[2], Field) - helper[0].wrap(Fieldset, 'legend', css_class="test-class") + helper[0].wrap(Fieldset, "legend", css_class="test-class") assert isinstance(layout[0], Fieldset) - assert layout[0].legend == 'legend' - assert layout[0][0] == 'email' + assert layout[0].legend == "legend" + assert layout[0][0] == "email" def test_wrap_together_with_slices(): helper = FormHelper() - layout = Layout( - 'email', - 'password1', - 'password2', - ) + layout = Layout("email", "password1", "password2",) helper.layout = layout helper[1:3].wrap_together(Field, css_class="test-class") - assert layout.fields[0] == 'email' + assert layout.fields[0] == "email" assert isinstance(layout.fields[1], Field) - assert layout.fields[1][0] == 'password1' - assert layout.fields[1][1] == 'password2' - - layout = Layout( - Div('email'), - 'password1', - 'password2', - ) + assert layout.fields[1][0] == "password1" + assert layout.fields[1][1] == "password2" + + layout = Layout(Div("email"), "password1", "password2",) helper.layout = layout helper[0:3].wrap_together(Field, css_class="test-class") assert isinstance(layout.fields[0], Field) assert isinstance(layout.fields[0][0], Div) - assert layout.fields[0][0][0] == 'email' - assert layout.fields[0][1] == 'password1' - assert layout.fields[0][2] == 'password2' - - layout = Layout( - 'email', - 'password1', - 'password2', - ) + assert layout.fields[0][0][0] == "email" + assert layout.fields[0][1] == "password1" + assert layout.fields[0][2] == "password2" + + layout = Layout("email", "password1", "password2",) helper.layout = layout helper[0].wrap_together(Field, css_class="test-class") assert isinstance(layout.fields[0], Field) - assert layout.fields[1] == 'password1' - assert layout.fields[2] == 'password2' - - layout = Layout( - 'email', - 'password1', - 'password2', - ) + assert layout.fields[1] == "password1" + assert layout.fields[2] == "password2" + + layout = Layout("email", "password1", "password2",) helper.layout = layout helper[0].wrap_together(Fieldset, "legend", css_class="test-class") assert isinstance(layout.fields[0], Fieldset) - assert layout.fields[0].legend == 'legend' - assert layout.fields[1] == 'password1' - assert layout.fields[2] == 'password2' + assert layout.fields[0].legend == "legend" + assert layout.fields[1] == "password1" + assert layout.fields[2] == "password2" def test_wrap_together_partial_slices(): helper = FormHelper() - layout = Layout( - 'email', - 'password1', - 'password2', - ) + layout = Layout("email", "password1", "password2",) helper.layout = layout helper[:2].wrap_together(Field, css_class="test-class") assert isinstance(layout.fields[0], Field) - assert layout.fields[1] == 'password2' - assert layout.fields[0][0] == 'email' - assert layout.fields[0][1] == 'password1' + assert layout.fields[1] == "password2" + assert layout.fields[0][0] == "email" + assert layout.fields[0][1] == "password1" helper = FormHelper() - layout = Layout( - 'email', - 'password1', - 'password2', - ) + layout = Layout("email", "password1", "password2",) helper.layout = layout helper[1:].wrap_together(Field, css_class="test-class") - assert layout.fields[0] == 'email' + assert layout.fields[0] == "email" assert isinstance(layout.fields[1], Field) - assert layout.fields[1][0] == 'password1' - assert layout.fields[1][1] == 'password2' + assert layout.fields[1][0] == "password1" + assert layout.fields[1][1] == "password2" def test_update_attributes(): helper = FormHelper() - helper.layout = Layout( - 'email', - Field('password1'), - 'password2', - ) - helper['password1'].update_attributes(readonly=True) - assert 'readonly' in helper.layout[1].attrs + helper.layout = Layout("email", Field("password1"), "password2",) + helper["password1"].update_attributes(readonly=True) + assert "readonly" in helper.layout[1].attrs def test_update_attributes_and_wrap_once(): helper = FormHelper() - layout = Layout( - 'email', - Field('password1'), - 'password2', - ) + layout = Layout("email", Field("password1"), "password2",) helper.layout = layout helper.filter(Field).update_attributes(readonly=True) assert isinstance(layout[1], Field) - assert layout[1].attrs == {'readonly': True} + assert layout[1].attrs == {"readonly": True} - layout = Layout( - 'email', - Div(Field('password1')), - 'password2', - ) + layout = Layout("email", Div(Field("password1")), "password2",) helper.layout = layout helper.filter(Field, max_level=2).update_attributes(readonly=True) assert isinstance(layout[1][0], Field) - assert layout[1][0].attrs == {'readonly': True} + assert layout[1][0].attrs == {"readonly": True} - layout = Layout( - 'email', - Div(Field('password1')), - 'password2', - ) + layout = Layout("email", Div(Field("password1")), "password2",) helper.layout = layout helper.filter(str, greedy=True).wrap_once(Field) @@ -178,191 +130,104 @@ def test_update_attributes_and_wrap_once(): assert isinstance(layout[1][0], Field) assert isinstance(layout[1][0][0], str) assert isinstance(layout[2], Field) - assert layout[1][0].attrs == {'readonly': True} - assert layout[0].attrs == {'readonly': True} - assert layout[2].attrs == {'readonly': True} + assert layout[1][0].attrs == {"readonly": True} + assert layout[0].attrs == {"readonly": True} + assert layout[2].attrs == {"readonly": True} def test_get_layout_objects(): - layout_1 = Layout( - Div() - ) - assert layout_1.get_layout_objects(Div) == [[[0], 'div']] - - layout_2 = Layout( - Div( - Div( - Div('email') - ), - Div('password1'), - 'password2' - ) - ) - assert layout_2.get_layout_objects(Div) == [[[0], 'div']] - assert layout_2.get_layout_objects(Div, max_level=1) == [ - [[0], 'div'], - [[0, 0], 'div'], - [[0, 1], 'div'] - ] + layout_1 = Layout(Div()) + assert layout_1.get_layout_objects(Div) == [[[0], "div"]] + + layout_2 = Layout(Div(Div(Div("email")), Div("password1"), "password2")) + assert layout_2.get_layout_objects(Div) == [[[0], "div"]] + assert layout_2.get_layout_objects(Div, max_level=1) == [[[0], "div"], [[0, 0], "div"], [[0, 1], "div"]] assert layout_2.get_layout_objects(Div, max_level=2) == [ - [[0], 'div'], - [[0, 0], 'div'], - [[0, 0, 0], 'div'], - [[0, 1], 'div'] + [[0], "div"], + [[0, 0], "div"], + [[0, 0, 0], "div"], + [[0, 1], "div"], ] - layout_3 = Layout( - 'email', - Div('password1'), - 'password2', - ) - assert layout_3.get_layout_objects(str, max_level=2) == [ - [[0], 'email'], - [[1, 0], 'password1'], - [[2], 'password2'] - ] + layout_3 = Layout("email", Div("password1"), "password2",) + assert layout_3.get_layout_objects(str, max_level=2) == [[[0], "email"], [[1, 0], "password1"], [[2], "password2"]] - layout_4 = Layout( - Div( - Div('field_name'), - 'field_name2', - ), - Div('password'), - 'extra_field' - ) - assert layout_4.get_layout_objects(Div) == [ - [[0], 'div'], - [[1], 'div'] - ] - assert layout_4.get_layout_objects(Div, max_level=1) == [ - [[0], 'div'], - [[0, 0], 'div'], - [[1], 'div'] - ] + layout_4 = Layout(Div(Div("field_name"), "field_name2",), Div("password"), "extra_field") + assert layout_4.get_layout_objects(Div) == [[[0], "div"], [[1], "div"]] + assert layout_4.get_layout_objects(Div, max_level=1) == [[[0], "div"], [[0, 0], "div"], [[1], "div"]] def test_filter_and_wrap(): helper = FormHelper() - layout = Layout( - 'email', - Div('password1'), - 'password2', - ) + layout = Layout("email", Div("password1"), "password2",) helper.layout = layout helper.filter(str).wrap(Field, css_class="test-class") assert isinstance(layout.fields[0], Field) assert isinstance(layout.fields[1], Div) assert isinstance(layout.fields[2], Field) - assert layout[2][0] == 'password2' + assert layout[2][0] == "password2" # Wrapping a div in a div helper.filter(Div).wrap(Div, css_class="test-class") assert isinstance(layout.fields[1], Div) assert isinstance(layout.fields[1].fields[0], Div) - assert layout[1][0][0] == 'password1' + assert layout[1][0][0] == "password1" def test_filter_and_wrap_side_effects(): helper = FormHelper() - layout = Layout( - Div( - 'extra_field', - Div('password1'), - ), - ) + layout = Layout(Div("extra_field", Div("password1"),),) helper.layout = layout with pytest.raises(DynamicError): helper.filter(Div, max_level=2).wrap(Div, css_class="test-class") def test_get_field_names(): - layout_1 = Div( - 'field_name' - ) - assert layout_1.get_field_names() == [ - [[0], 'field_name'] - ] + layout_1 = Div("field_name") + assert layout_1.get_field_names() == [[[0], "field_name"]] - layout_2 = Div( - Div('field_name') - ) - assert layout_2.get_field_names() == [ - [[0, 0], 'field_name'] - ] + layout_2 = Div(Div("field_name")) + assert layout_2.get_field_names() == [[[0, 0], "field_name"]] - layout_3 = Div( - Div('field_name'), - 'password' - ) - assert layout_3.get_field_names() == [ - [[0, 0], 'field_name'], - [[1], 'password'] - ] + layout_3 = Div(Div("field_name"), "password") + assert layout_3.get_field_names() == [[[0, 0], "field_name"], [[1], "password"]] - layout_4 = Div( - Div( - Div('field_name'), - 'field_name2', - ), - Div('password'), - 'extra_field' - ) + layout_4 = Div(Div(Div("field_name"), "field_name2",), Div("password"), "extra_field") assert layout_4.get_field_names() == [ - [[0, 0, 0], 'field_name'], - [[0, 1], 'field_name2'], - [[1, 0], 'password'], - [[2], 'extra_field'] + [[0, 0, 0], "field_name"], + [[0, 1], "field_name2"], + [[1, 0], "password"], + [[2], "extra_field"], ] - layout_5 = Div( - Div( - 'field_name', - 'field_name2', - ), - 'extra_field' - ) + layout_5 = Div(Div("field_name", "field_name2",), "extra_field") assert layout_5.get_field_names() == [ - [[0, 0], 'field_name'], - [[0, 1], 'field_name2'], - [[1], 'extra_field'], + [[0, 0], "field_name"], + [[0, 1], "field_name2"], + [[1], "extra_field"], ] def test_layout_get_field_names(): - layout_1 = Layout( - Div('field_name'), - 'password' - ) + layout_1 = Layout(Div("field_name"), "password") assert layout_1.get_field_names() == [ - [[0, 0], 'field_name'], - [[1], 'password'], + [[0, 0], "field_name"], + [[1], "password"], ] - layout_2 = Layout( - Div('field_name'), - 'password', - Fieldset('legend', 'extra_field') - ) + layout_2 = Layout(Div("field_name"), "password", Fieldset("legend", "extra_field")) assert layout_2.get_field_names() == [ - [[0, 0], 'field_name'], - [[1], 'password'], - [[2, 0], 'extra_field'], + [[0, 0], "field_name"], + [[1], "password"], + [[2, 0], "extra_field"], ] - layout_3 = Layout( - Div( - Div( - Div('email') - ), - Div('password1'), - 'password2' - ) - ) + layout_3 = Layout(Div(Div(Div("email")), Div("password1"), "password2")) assert layout_3.get_field_names() == [ - [[0, 0, 0, 0], 'email'], - [[0, 1, 0], 'password1'], - [[0, 2], 'password2'], + [[0, 0, 0, 0], "email"], + [[0, 1, 0], "password1"], + [[0, 2], "password2"], ] @@ -371,8 +236,8 @@ def test_filter_by_widget(advanced_layout): form.helper = FormHelper(form) form.helper.layout = advanced_layout assert form.helper.filter_by_widget(forms.PasswordInput).slice == [ - [[0, 1, 0, 0], 'password1'], - [[0, 4, 0], 'password2'], + [[0, 1, 0, 0], "password1"], + [[0, 4, 0], "password2"], ] @@ -381,9 +246,9 @@ def test_exclude_by_widget(advanced_layout): form.helper = FormHelper(form) form.helper.layout = advanced_layout assert form.helper.exclude_by_widget(forms.PasswordInput).slice == [ - [[0, 0, 0, 0], 'email'], - [[0, 3, 0], 'first_name'], - [[1], 'last_name'], + [[0, 0, 0, 0], "email"], + [[0, 3, 0], "first_name"], + [[1], "last_name"], ] @@ -391,7 +256,7 @@ def test_exclude_by_widget_and_wrap(advanced_layout): form = SampleForm() form.helper = FormHelper(form) form.helper.layout = advanced_layout - form.helper.exclude_by_widget(forms.PasswordInput).wrap(Field, css_class='hero') + form.helper.exclude_by_widget(forms.PasswordInput).wrap(Field, css_class="hero") # Check wrapped fields assert isinstance(form.helper.layout[0][0][0][0], Field) assert isinstance(form.helper.layout[0][3][0], Field) @@ -419,64 +284,44 @@ def test_filter_by_widget_without_form(advanced_layout): def test_formhelper__getitem__(): helper = FormHelper() - layout = Layout( - Div('email'), - 'password1', - ) + layout = Layout(Div("email"), "password1",) helper.layout = layout - helper['email'].wrap(Field, css_class='hero') + helper["email"].wrap(Field, css_class="hero") assert isinstance(layout[0][0], Field) - assert layout[0][0][0] == 'email' + assert layout[0][0][0] == "email" helper = FormHelper() - helper.layout = Layout('password1') - helper['password1'].wrap(AppendedText, "extra") + helper.layout = Layout("password1") + helper["password1"].wrap(AppendedText, "extra") assert isinstance(helper.layout[0], AppendedText) - assert helper.layout[0][0] == 'password1' - assert helper.layout[0].text == 'extra' + assert helper.layout[0][0] == "password1" + assert helper.layout[0].text == "extra" def test_formhelper__setitem__(): helper = FormHelper() - layout = Layout( - 'first_field', - Div('email') - ) + layout = Layout("first_field", Div("email")) helper.layout = layout - helper[0] = 'replaced' - assert layout[0] == 'replaced' + helper[0] = "replaced" + assert layout[0] == "replaced" def test_formhelper__delitem__and__len__(): helper = FormHelper() - layout = Layout( - 'first_field', - Div('email') - ) + layout = Layout("first_field", Div("email")) helper.layout = layout del helper[0] assert len(helper) == 1 def test__delitem__and__len__layout_object(): - layout = Layout( - 'first_field', - Div('email') - ) + layout = Layout("first_field", Div("email")) del layout[0] assert len(layout) == 1 def test__getitem__layout_object(): - layout = Layout( - Div( - Div( - Div('email') - ), - Div('password1'), - 'password2' - ) - ) + layout = Layout(Div(Div(Div("email")), Div("password1"), "password2")) assert isinstance(layout[0], Div) assert isinstance(layout[0][0], Div) assert isinstance(layout[0][0][0], Div) @@ -486,44 +331,24 @@ def test__getitem__layout_object(): def test__getattr__append_layout_object(): - layout = Layout( - Div('email') - ) - layout.append('password1') + layout = Layout(Div("email")) + layout.append("password1") assert isinstance(layout[0], Div) assert isinstance(layout[0][0], str) assert isinstance(layout[1], str) def test__setitem__layout_object(): - layout = Layout( - Div('email') - ) - layout[0][0] = 'password1' + layout = Layout(Div("email")) + layout[0][0] = "password1" assert isinstance(layout[0], Div) - assert layout[0][0] == 'password1' + assert layout[0][0] == "password1" @only_uni_form def test_filter(): helper = FormHelper() - helper.layout = Layout( - Div( - MultiField('field_name'), - 'field_name2', - ), - Div('password'), - 'extra_field' - ) - assert helper.filter(Div, MultiField).slice == [ - [[0], 'div'], - [[1], 'div'] - ] - assert helper.filter(Div, MultiField, max_level=1).slice == [ - [[0], 'div'], - [[0, 0], 'multifield'], - [[1], 'div'] - ] - assert helper.filter(MultiField, max_level=1).slice == [ - [[0, 0], 'multifield'] - ] + helper.layout = Layout(Div(MultiField("field_name"), "field_name2",), Div("password"), "extra_field") + assert helper.filter(Div, MultiField).slice == [[[0], "div"], [[1], "div"]] + assert helper.filter(Div, MultiField, max_level=1).slice == [[[0], "div"], [[0, 0], "multifield"], [[1], "div"]] + assert helper.filter(MultiField, max_level=1).slice == [[[0, 0], "multifield"]] diff --git a/crispy_forms/tests/test_form_helper.py b/crispy_forms/tests/test_form_helper.py index f0e27d95f..3979277f5 100644 --- a/crispy_forms/tests/test_form_helper.py +++ b/crispy_forms/tests/test_form_helper.py @@ -23,36 +23,38 @@ def test_inputs(settings): form_helper = FormHelper() - form_helper.add_input(Submit('my-submit', 'Submit', css_class="button white")) - form_helper.add_input(Reset('my-reset', 'Reset')) - form_helper.add_input(Hidden('my-hidden', 'Hidden')) - form_helper.add_input(Button('my-button', 'Button')) + form_helper.add_input(Submit("my-submit", "Submit", css_class="button white")) + form_helper.add_input(Reset("my-reset", "Reset")) + form_helper.add_input(Hidden("my-hidden", "Hidden")) + form_helper.add_input(Button("my-button", "Button")) - template = Template(""" + template = Template( + """ {% load crispy_forms_tags %} {% crispy form form_helper %} - """) - c = Context({'form': SampleForm(), 'form_helper': form_helper}) + """ + ) + c = Context({"form": SampleForm(), "form_helper": form_helper}) html = template.render(c) - assert 'button white' in html + assert "button white" in html assert 'id="submit-id-my-submit"' in html assert 'id="reset-id-my-reset"' in html assert 'name="my-hidden"' in html assert 'id="button-id-my-button"' in html - if settings.CRISPY_TEMPLATE_PACK == 'uni_form': - assert 'submit submitButton' in html - assert 'reset resetButton' in html + if settings.CRISPY_TEMPLATE_PACK == "uni_form": + assert "submit submitButton" in html + assert "reset resetButton" in html assert 'class="button"' in html else: assert 'class="btn"' in html - assert 'btn btn-primary' in html - assert 'btn btn-inverse' in html - if settings.CRISPY_TEMPLATE_PACK == 'bootstrap4': - assert len(re.findall(r']+> <', html)) == 9 + assert "btn btn-primary" in html + assert "btn btn-inverse" in html + if settings.CRISPY_TEMPLATE_PACK == "bootstrap4": + assert len(re.findall(r"]+> <", html)) == 9 else: - assert len(re.findall(r']+> <', html)) == 8 + assert len(re.findall(r"]+> <", html)) == 8 def test_invalid_form_method(): @@ -63,31 +65,33 @@ def test_invalid_form_method(): def test_form_with_helper_without_layout(settings): form_helper = FormHelper() - form_helper.form_id = 'this-form-rocks' - form_helper.form_class = 'forms-that-rock' - form_helper.form_method = 'GET' - form_helper.form_action = 'simpleAction' - form_helper.form_error_title = 'ERRORS' - - template = Template(""" + form_helper.form_id = "this-form-rocks" + form_helper.form_class = "forms-that-rock" + form_helper.form_method = "GET" + form_helper.form_action = "simpleAction" + form_helper.form_error_title = "ERRORS" + + template = Template( + """ {% load crispy_forms_tags %} {% crispy testForm form_helper %} - """) + """ + ) # now we render it, with errors - form = SampleForm({'password1': 'wargame', 'password2': 'god'}) + form = SampleForm({"password1": "wargame", "password2": "god"}) form.is_valid() - c = Context({'testForm': form, 'form_helper': form_helper}) + c = Context({"testForm": form, "form_helper": form_helper}) html = template.render(c) # Lets make sure everything loads right - assert html.count('Passwords dont match' in html - assert str(_('This field is required.')) in html - assert 'error' in html + assert "
  • Passwords dont match
  • " in html + assert str(_("This field is required.")) in html + assert "error" in html # Now we render without errors form.helper.form_show_errors = False - c = Context({'testForm': form}) + c = Context({"testForm": form}) html = template.render(c) # Ensure errors were not rendered - assert '
  • Passwords dont match
  • ' not in html - assert str(_('This field is required.')) not in html - assert 'error' not in html + assert "
  • Passwords dont match
  • " not in html + assert str(_("This field is required.")) not in html + assert "error" not in html def test_html5_required(): @@ -143,7 +149,7 @@ def test_html5_required(): if django.VERSION < (1, 10): assert html.count('required="required"') == 7 else: - assert len(re.findall(r'\brequired\b', html)) == 7 + assert len(re.findall(r"\brequired\b", html)) == 7 form = SampleForm() form.helper = FormHelper() @@ -154,83 +160,83 @@ def test_html5_required(): def test_media_is_included_by_default_with_uniform(): form = SampleFormWithMedia() form.helper = FormHelper() - form.helper.template_pack = 'uni_form' + form.helper.template_pack = "uni_form" html = render_crispy_form(form) - assert 'test.css' in html - assert 'test.js' in html + assert "test.css" in html + assert "test.js" in html def test_media_is_included_by_default_with_bootstrap(): form = SampleFormWithMedia() form.helper = FormHelper() - form.helper.template_pack = 'bootstrap' + form.helper.template_pack = "bootstrap" html = render_crispy_form(form) - assert 'test.css' in html - assert 'test.js' in html + assert "test.css" in html + assert "test.js" in html def test_media_is_included_by_default_with_bootstrap3(): form = SampleFormWithMedia() form.helper = FormHelper() - form.helper.template_pack = 'bootstrap3' + form.helper.template_pack = "bootstrap3" html = render_crispy_form(form) - assert 'test.css' in html - assert 'test.js' in html + assert "test.css" in html + assert "test.js" in html def test_media_is_included_by_default_with_bootstrap4(): form = SampleFormWithMedia() form.helper = FormHelper() - form.helper.template_pack = 'bootstrap4' + form.helper.template_pack = "bootstrap4" html = render_crispy_form(form) - assert 'test.css' in html - assert 'test.js' in html + assert "test.css" in html + assert "test.js" in html def test_media_removed_when_include_media_is_false_with_uniform(): form = SampleFormWithMedia() form.helper = FormHelper() - form.helper.template_pack = 'uni_form' + form.helper.template_pack = "uni_form" form.helper.include_media = False html = render_crispy_form(form) - assert 'test.css' not in html - assert 'test.js' not in html + assert "test.css" not in html + assert "test.js" not in html def test_media_removed_when_include_media_is_false_with_bootstrap(): form = SampleFormWithMedia() form.helper = FormHelper() - form.helper.template_pack = 'bootstrap' + form.helper.template_pack = "bootstrap" form.helper.include_media = False html = render_crispy_form(form) - assert 'test.css' not in html - assert 'test.js' not in html + assert "test.css" not in html + assert "test.js" not in html def test_media_removed_when_include_media_is_false_with_bootstrap3(): form = SampleFormWithMedia() form.helper = FormHelper() - form.helper.template_pack = 'bootstrap3' + form.helper.template_pack = "bootstrap3" form.helper.include_media = False html = render_crispy_form(form) - assert 'test.css' not in html - assert 'test.js' not in html + assert "test.css" not in html + assert "test.js" not in html def test_media_removed_when_include_media_is_false_with_bootstrap4(): form = SampleFormWithMedia() form.helper = FormHelper() - form.helper.template_pack = 'bootstrap4' + form.helper.template_pack = "bootstrap4" form.helper.include_media = False html = render_crispy_form(form) - assert 'test.css' not in html - assert 'test.js' not in html + assert "test.css" not in html + assert "test.js" not in html def test_attrs(): form = SampleForm() form.helper = FormHelper() - form.helper.attrs = {'id': 'TestIdForm', 'autocomplete': "off"} + form.helper.attrs = {"id": "TestIdForm", "autocomplete": "off"} html = render_crispy_form(form) assert 'autocomplete="off"' in html @@ -240,112 +246,124 @@ def test_attrs(): def test_template_context(): helper = FormHelper() helper.attrs = { - 'id': 'test-form', - 'class': 'test-forms', - 'action': 'submit/test/form', - 'autocomplete': 'off', + "id": "test-form", + "class": "test-forms", + "action": "submit/test/form", + "autocomplete": "off", } - node = CrispyFormNode('form', 'helper') + node = CrispyFormNode("form", "helper") context = node.get_response_dict(helper, {}, False) - assert context['form_id'] == "test-form" - assert context['form_attrs']['id'] == "test-form" - assert "test-forms" in context['form_class'] - assert "test-forms" in context['form_attrs']['class'] - assert context['form_action'] == "submit/test/form" - assert context['form_attrs']['action'] == "submit/test/form" - assert context['form_attrs']['autocomplete'] == "off" + assert context["form_id"] == "test-form" + assert context["form_attrs"]["id"] == "test-form" + assert "test-forms" in context["form_class"] + assert "test-forms" in context["form_attrs"]["class"] + assert context["form_action"] == "submit/test/form" + assert context["form_attrs"]["action"] == "submit/test/form" + assert context["form_attrs"]["autocomplete"] == "off" def test_template_context_using_form_attrs(): helper = FormHelper() - helper.form_id = 'test-form' - helper.form_class = 'test-forms' - helper.form_action = 'submit/test/form' - node = CrispyFormNode('form', 'helper') + helper.form_id = "test-form" + helper.form_class = "test-forms" + helper.form_action = "submit/test/form" + node = CrispyFormNode("form", "helper") context = node.get_response_dict(helper, {}, False) - assert context['form_id'] == "test-form" - assert context['form_attrs']['id'] == "test-form" - assert "test-forms" in context['form_class'] - assert "test-forms" in context['form_attrs']['class'] - assert context['form_action'] == "submit/test/form" - assert context['form_attrs']['action'] == "submit/test/form" + assert context["form_id"] == "test-form" + assert context["form_attrs"]["id"] == "test-form" + assert "test-forms" in context["form_class"] + assert "test-forms" in context["form_attrs"]["class"] + assert context["form_action"] == "submit/test/form" + assert context["form_attrs"]["action"] == "submit/test/form" def test_template_helper_access(): helper = FormHelper() - helper.form_id = 'test-form' + helper.form_id = "test-form" - assert helper['form_id'] == 'test-form' + assert helper["form_id"] == "test-form" def test_without_helper(settings): - template = Template(""" + template = Template( + """ {% load crispy_forms_tags %} {% crispy form %} - """) - c = Context({'form': SampleForm()}) + """ + ) + c = Context({"form": SampleForm()}) html = template.render(c) # Lets make sure everything loads right - assert '') assert contains_partial(html, '') @@ -503,19 +520,17 @@ def test_render_hidden_fields(): def test_render_required_fields(): test_form = SampleForm() test_form.helper = FormHelper() - test_form.helper.layout = Layout( - 'email' - ) + test_form.helper.layout = Layout("email") test_form.helper.render_required_fields = True html = render_crispy_form(test_form) - assert html.count('Special custom form" in html @@ -524,11 +539,8 @@ def test_helper_custom_template(): def test_helper_custom_field_template(): form = SampleForm() form.helper = FormHelper() - form.helper.layout = Layout( - 'password1', - 'password2', - ) - form.helper.field_template = 'custom_field_template.html' + form.helper.layout = Layout("password1", "password2",) + form.helper.field_template = "custom_field_template.html" html = render_crispy_form(form) assert html.count("

    Special custom field

    ") == 2 @@ -537,7 +549,7 @@ def test_helper_custom_field_template(): def test_helper_custom_field_template_no_layout(): form = SampleForm() form.helper = FormHelper() - form.helper.field_template = 'custom_field_template.html' + form.helper.field_template = "custom_field_template.html" html = render_crispy_form(form) for field in form.fields: @@ -556,167 +568,153 @@ def test_helper_std_field_template_no_layout(): @only_uni_form def test_form_show_errors(): - form = SampleForm({ - 'email': 'invalidemail', - 'first_name': 'first_name_too_long', - 'last_name': 'last_name_too_long', - 'password1': 'yes', - 'password2': 'yes', - }) + form = SampleForm( + { + "email": "invalidemail", + "first_name": "first_name_too_long", + "last_name": "last_name_too_long", + "password1": "yes", + "password2": "yes", + } + ) form.helper = FormHelper() form.helper.layout = Layout( - Field('email'), - Field('first_name'), - Field('last_name'), - Field('password1'), - Field('password2'), + Field("email"), Field("first_name"), Field("last_name"), Field("password1"), Field("password2"), ) form.is_valid() form.helper.form_show_errors = True html = render_crispy_form(form) - assert html.count('error') == 9 + assert html.count("error") == 9 form.helper.form_show_errors = False html = render_crispy_form(form) - assert html.count('error') == 0 + assert html.count("error") == 0 @only_uni_form def test_multifield_errors(): - form = SampleForm({ - 'email': 'invalidemail', - 'password1': 'yes', - 'password2': 'yes', - }) + form = SampleForm({"email": "invalidemail", "password1": "yes", "password2": "yes",}) form.helper = FormHelper() - form.helper.layout = Layout( - MultiField('legend', 'email') - ) + form.helper.layout = Layout(MultiField("legend", "email")) form.is_valid() form.helper.form_show_errors = True html = render_crispy_form(form) - assert html.count('error') == 3 + assert html.count("error") == 3 # Reset layout for avoiding side effects - form.helper.layout = Layout( - MultiField('legend', 'email') - ) + form.helper.layout = Layout(MultiField("legend", "email")) form.helper.form_show_errors = False html = render_crispy_form(form) - assert html.count('error') == 0 + assert html.count("error") == 0 @only_bootstrap3 def test_bootstrap_form_show_errors_bs3(): - form = SampleForm({ - 'email': 'invalidemail', - 'first_name': 'first_name_too_long', - 'last_name': 'last_name_too_long', - 'password1': 'yes', - 'password2': 'yes', - }) + form = SampleForm( + { + "email": "invalidemail", + "first_name": "first_name_too_long", + "last_name": "last_name_too_long", + "password1": "yes", + "password2": "yes", + } + ) form.helper = FormHelper() form.helper.layout = Layout( - AppendedText('email', 'whatever'), - PrependedText('first_name', 'blabla'), - PrependedAppendedText('last_name', 'foo', 'bar'), - AppendedText('password1', 'whatever'), - PrependedText('password2', 'blabla'), + AppendedText("email", "whatever"), + PrependedText("first_name", "blabla"), + PrependedAppendedText("last_name", "foo", "bar"), + AppendedText("password1", "whatever"), + PrependedText("password2", "blabla"), ) form.is_valid() form.helper.form_show_errors = True html = render_crispy_form(form) - assert html.count('error') == 6 + assert html.count("error") == 6 form.helper.form_show_errors = False html = render_crispy_form(form) - assert html.count('error') == 0 + assert html.count("error") == 0 @only_bootstrap4 def test_bootstrap_form_show_errors_bs4(): - form = SampleForm({ - 'email': 'invalidemail', - 'first_name': 'first_name_too_long', - 'last_name': 'last_name_too_long', - 'password1': 'yes', - 'password2': 'yes', - }) + form = SampleForm( + { + "email": "invalidemail", + "first_name": "first_name_too_long", + "last_name": "last_name_too_long", + "password1": "yes", + "password2": "yes", + } + ) form.helper = FormHelper() form.helper.layout = Layout( - AppendedText('email', 'whatever'), - PrependedText('first_name', 'blabla'), - PrependedAppendedText('last_name', 'foo', 'bar'), - AppendedText('password1', 'whatever'), - PrependedText('password2', 'blabla'), + AppendedText("email", "whatever"), + PrependedText("first_name", "blabla"), + PrependedAppendedText("last_name", "foo", "bar"), + AppendedText("password1", "whatever"), + PrependedText("password2", "blabla"), ) form.is_valid() form.helper.form_show_errors = True html = render_crispy_form(form) - assert html.count('error') == 3 + assert html.count("error") == 3 form.helper.form_show_errors = False html = render_crispy_form(form) - assert html.count('error') == 0 + assert html.count("error") == 0 @only_bootstrap def test_error_text_inline(settings): - form = SampleForm({'email': 'invalidemail'}) + form = SampleForm({"email": "invalidemail"}) form.helper = FormHelper() layout = Layout( - AppendedText('first_name', 'wat'), - PrependedText('email', '@'), - PrependedAppendedText('last_name', '@', 'wat'), + AppendedText("first_name", "wat"), PrependedText("email", "@"), PrependedAppendedText("last_name", "@", "wat"), ) form.helper.layout = layout form.is_valid() html = render_crispy_form(form) - help_class = 'help-inline' - help_tag_name = 'p' - if settings.CRISPY_TEMPLATE_PACK == 'bootstrap3': - help_class = 'help-block' - elif settings.CRISPY_TEMPLATE_PACK == 'bootstrap4': - help_class = 'invalid-feedback' - help_tag_name = 'div' + help_class = "help-inline" + help_tag_name = "p" + if settings.CRISPY_TEMPLATE_PACK == "bootstrap3": + help_class = "help-block" + elif settings.CRISPY_TEMPLATE_PACK == "bootstrap4": + help_class = "invalid-feedback" + help_tag_name = "div" - matches = re.findall( - r'