From 78119dc6b3089498c42565b2e37082bde79d70c2 Mon Sep 17 00:00:00 2001 From: Tobias Kunze Date: Mon, 17 Mar 2025 22:38:26 +0100 Subject: [PATCH] Build template-based form rendering with bootstrap attrs --- src/servala/frontend/forms/renderers.py | 28 +++++++++++++++++++ .../frontend/templates/account/login.html | 5 +++- .../templates/frontend/forms/form.html | 19 +++++++++++++ .../frontend/forms/vertical_field.html | 26 +++++++++++++++++ src/servala/settings.py | 2 ++ 5 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 src/servala/frontend/forms/renderers.py create mode 100644 src/servala/frontend/templates/frontend/forms/form.html create mode 100644 src/servala/frontend/templates/frontend/forms/vertical_field.html diff --git a/src/servala/frontend/forms/renderers.py b/src/servala/frontend/forms/renderers.py new file mode 100644 index 0000000..f8cdb5d --- /dev/null +++ b/src/servala/frontend/forms/renderers.py @@ -0,0 +1,28 @@ +from django.forms.renderers import TemplatesSetting + + +def inject_class(f, class_name): + def inner(*args, **kwargs): + result = f(*args, **kwargs) + class_list = result.get("class", "") + class_list = f"{class_list} {class_name}".strip() + result["class"] = class_list + return result + + return inner + + +class VerticalFormRenderer(TemplatesSetting): + form_template_name = "frontend/forms/form.html" + field_template_name = "frontend/forms/vertical_field.html" + + def render(self, template_name, context, request=None): + if field := context.get("field"): + if field.field.widget.input_type == "checkbox": + class_name = "form-check-input" + else: + class_name = "form-control" + field.build_widget_attrs = inject_class( + field.build_widget_attrs, class_name + ) + return super().render(template_name, context, request) diff --git a/src/servala/frontend/templates/account/login.html b/src/servala/frontend/templates/account/login.html index b0242b9..214b77d 100644 --- a/src/servala/frontend/templates/account/login.html +++ b/src/servala/frontend/templates/account/login.html @@ -37,7 +37,10 @@ {% translate "Log in with email and password instead" %} -
+
{% csrf_token %} {{ form }} diff --git a/src/servala/frontend/templates/frontend/forms/form.html b/src/servala/frontend/templates/frontend/forms/form.html new file mode 100644 index 0000000..a9e8c47 --- /dev/null +++ b/src/servala/frontend/templates/frontend/forms/form.html @@ -0,0 +1,19 @@ +{% if errors %} + +{% endif %} +
+
+ {% for field, errors in fields %}{{ field.as_field_group }}{% endfor %} + {% for field in hidden_fields %}{{ field }}{% endfor %} +
+
diff --git a/src/servala/frontend/templates/frontend/forms/vertical_field.html b/src/servala/frontend/templates/frontend/forms/vertical_field.html new file mode 100644 index 0000000..bcbf8d6 --- /dev/null +++ b/src/servala/frontend/templates/frontend/forms/vertical_field.html @@ -0,0 +1,26 @@ +{% load i18n %} +
+
+ {% if field.field.widget.input_type != "checkbox" or field.field.widget.allow_multiple_selected %} + + {% endif %} + {% if field.use_fieldset %} +
+ {% endif %} + {{ field }} + {% if field.field.widget.input_type == "checkbox" and not field.field.widget.allow_multiple_selected %} + + {% endif %} + {% if field.use_fieldset %}
{% endif %} + {% for text in field.errors %}
{{ text }}
{% endfor %} + {% if field.help_text %} + {{ field.help_text|safe }} + {% endif %} +
+
diff --git a/src/servala/settings.py b/src/servala/settings.py index 73c771b..eba2e0a 100644 --- a/src/servala/settings.py +++ b/src/servala/settings.py @@ -95,6 +95,7 @@ INSTALLED_APPS = [ "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", + "django.forms", "servala.frontend", "allauth", "allauth.account", @@ -151,6 +152,7 @@ TEMPLATES = [ }, ] +FORM_RENDERER = "servala.frontend.forms.renderers.VerticalFormRenderer" MESSAGE_TAGS = { messages.ERROR: "danger", }