From 4ca87b6a128a6656c465e48de02de94d0c74aaf8 Mon Sep 17 00:00:00 2001 From: Dylan Verheul Date: Wed, 24 Apr 2024 13:33:03 +0200 Subject: [PATCH] Cleanup Django versions (#717) --- src/bootstrap4/renderers.py | 23 ++++---------- src/bootstrap4/utils.py | 3 -- tests/test_forms.py | 61 +++++++++++++++++++++++++++---------- tox.ini | 2 -- 4 files changed, 51 insertions(+), 38 deletions(-) diff --git a/src/bootstrap4/renderers.py b/src/bootstrap4/renderers.py index 54ae9800..dda3fcb6 100644 --- a/src/bootstrap4/renderers.py +++ b/src/bootstrap4/renderers.py @@ -31,7 +31,7 @@ render_label, ) from .text import text_value -from .utils import DJANGO_VERSION, add_css_class, render_template_file +from .utils import add_css_class, render_template_file try: # If Django is set up without a database, importing this widget gives RuntimeError @@ -322,22 +322,11 @@ def add_widget_attrs(self): def list_to_class(self, html, klass): classes = add_css_class(klass, self.get_size_class()) - if DJANGO_VERSION >= 4: - soup = BeautifulSoup(html, features="html.parser") - enclosing_div = soup.find("div") - enclosing_div.attrs["class"] = classes - for inner_div in enclosing_div.find_all("div"): - inner_div.attrs["class"] = inner_div.attrs.get("class", []) + [self.form_check_class] - else: - mapping = [ - ("", ""), - ("", ""), - ] - for k, v in mapping: - html = html.replace(k, v) - soup = BeautifulSoup(html, features="html.parser") + soup = BeautifulSoup(html, features="html.parser") + enclosing_div = soup.find("div") + enclosing_div.attrs["class"] = classes + for inner_div in enclosing_div.find_all("div"): + inner_div.attrs["class"] = inner_div.attrs.get("class", []) + [self.form_check_class] # Apply bootstrap4 classes to labels and inputs. # A simple 'replace' isn't enough as we don't want to have several 'class' attr definition, which would happen # if we tried to 'html.replace("input", "input class=...")' diff --git a/src/bootstrap4/utils.py b/src/bootstrap4/utils.py index 76807b47..82472e3e 100644 --- a/src/bootstrap4/utils.py +++ b/src/bootstrap4/utils.py @@ -2,7 +2,6 @@ from collections.abc import Mapping from urllib.parse import parse_qs, urlparse, urlunparse -from django import get_version from django.forms.utils import flatatt from django.template.base import FilterExpression, TemplateSyntaxError, Variable, VariableDoesNotExist, kwarg_re from django.template.loader import get_template @@ -13,8 +12,6 @@ from .text import text_value -DJANGO_VERSION = int(get_version()[:1]) - # RegEx for quoted string QUOTED_STRING = re.compile(r'^["\'](?P.+)["\']$') diff --git a/tests/test_forms.py b/tests/test_forms.py index 0da833c8..5ee2de53 100644 --- a/tests/test_forms.py +++ b/tests/test_forms.py @@ -4,7 +4,6 @@ from django.utils.html import escape from bootstrap4.exceptions import BootstrapError -from bootstrap4.utils import DJANGO_VERSION from .forms import CharFieldTestForm, TestForm from .utils import render_field, render_form_field, render_template_with_form @@ -37,7 +36,10 @@ def test_help_with_quotes(self): res = render_form_field("sender") self.assertIn('title="{}"'.format(escape(TestForm.base_fields["sender"].help_text)), res) res = render_form_field("cc_myself") - self.assertIn('title="{}"'.format(escape(TestForm.base_fields["cc_myself"].help_text)), res) + self.assertIn( + 'title="{}"'.format(escape(TestForm.base_fields["cc_myself"].help_text)), + res, + ) def test_subject(self): res = render_form_field("subject") @@ -54,7 +56,10 @@ def test_xss_field(self): ), res, ) - self.assertIn(('placeholder="XSS" onmouseover="alert('Hello, XSS')" foo=""'), res) + self.assertIn( + ('placeholder="XSS" onmouseover="alert('Hello, XSS')" foo=""'), + res, + ) def test_password(self): res = render_form_field("password") @@ -83,10 +88,9 @@ def test_radio_select(self): "" "" ) - if DJANGO_VERSION >= 4: - expected_html = expected_html.replace( - '', "" - ) + expected_html = expected_html.replace( + '', "" + ) self.assertHTMLEqual(res, expected_html) def test_checkbox(self): @@ -97,21 +101,37 @@ def test_checkbox(self): res = BeautifulSoup(res, "html.parser") form_group = self._select_one_element(res, ".form-group", "Checkbox should be rendered inside a .form-group.") form_check = self._select_one_element( - form_group, ".form-check", "There should be a .form-check inside .form-group" + form_group, + ".form-check", + "There should be a .form-check inside .form-group", ) checkbox = self._select_one_element(form_check, "input", "The checkbox should be inside the .form-check") - self.assertIn("form-check-input", checkbox["class"], "The checkbox should have the class 'form-check-input'.") + self.assertIn( + "form-check-input", + checkbox["class"], + "The checkbox should have the class 'form-check-input'.", + ) label = checkbox.next_sibling self.assertIsNotNone(label, "The label should be rendered after the checkbox.") self.assertEqual(label.name, "label", "After the checkbox there should be a label.") self.assertEqual( - label["for"], checkbox["id"], "The for attribute of the label should be the id of the checkbox." + label["for"], + checkbox["id"], + "The for attribute of the label should be the id of the checkbox.", ) help_text = label.next_sibling self.assertIsNotNone(help_text, "The help text should be rendered after the label.") self.assertEqual(help_text.name, "small", "The help text should be rendered as tag.") - self.assertIn("form-text", help_text["class"], "The help text should have the class 'form-text'.") - self.assertIn("text-muted", help_text["class"], "The help text should have the class 'text-muted'.") + self.assertIn( + "form-text", + help_text["class"], + "The help text should have the class 'form-text'.", + ) + self.assertIn( + "text-muted", + help_text["class"], + "The help text should have the class 'text-muted'.", + ) def test_checkbox_multiple_select(self): res = render_form_field("category2") @@ -183,7 +203,10 @@ def test_input_group_addon_empty(self): res = render_template_with_form('{% bootstrap_field form.subject addon_before=None addon_after="after" %}') # noqa self.assertIn('class="input-group"', res) self.assertNotIn("input-group-prepend", res) - self.assertIn('
after
', res) + self.assertIn( + '
after
', + res, + ) def test_input_group_addon_validation(self): """ @@ -194,7 +217,8 @@ def test_input_group_addon_validation(self): # invalid form data: data = {"subject": ""} res = render_template_with_form( - '{% bootstrap_field form.subject addon_before=None addon_after="after" %}', data=data + '{% bootstrap_field form.subject addon_before=None addon_after="after" %}', + data=data, ) # noqa res = BeautifulSoup(res, "html.parser") self._select_one_element( @@ -204,7 +228,9 @@ def test_input_group_addon_validation(self): "required, must be placed inside the input-group", ) self._select_one_element( - res, ".form-group > .form-text", "The form-text message must be placed inside the form-group" + res, + ".form-group > .form-text", + "The form-text message must be placed inside the form-group", ) self.assertEqual( len(res.select(".form-group > .invalid-feedback")), @@ -288,5 +314,8 @@ def test_show_label_skip(self): def test_for_formset(self): TestFormSet = formset_factory(CharFieldTestForm, extra=1) test_formset = TestFormSet() - res = render_template_with_form("{% bootstrap_formset formset show_label=False %}", {"formset": test_formset}) + res = render_template_with_form( + "{% bootstrap_formset formset show_label=False %}", + {"formset": test_formset}, + ) self.assertIn("sr-only", res) diff --git a/tox.ini b/tox.ini index 5673c0b8..94b9eee6 100644 --- a/tox.ini +++ b/tox.ini @@ -24,8 +24,6 @@ setenv = commands = python manage.py test {posargs} deps = - 4.0: Django==4.0.* - 4.1: Django==4.1.* 4.2: Django==4.2.* 5.0: Django==5.0.* main: https://github.com/django/django/archive/main.tar.gz