* adds configurable label-input ratio setting for forms and specializes for RemoveModalForm * enhances form body html structure for better UX and usage of label-input ratio
169 lines
5.1 KiB
Python
169 lines
5.1 KiB
Python
"""
|
|
Author: Michel Peltriaux
|
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
Created on: 15.08.22
|
|
|
|
"""
|
|
from abc import abstractmethod
|
|
|
|
from django import forms
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from compensation.models import EcoAccount
|
|
from konova.models import BaseObject
|
|
|
|
|
|
class BaseForm(forms.Form):
|
|
"""
|
|
Basic form for that holds attributes needed in all other forms
|
|
"""
|
|
template = None
|
|
action_url = None
|
|
action_btn_label = _("Save")
|
|
form_title = None
|
|
cancel_redirect = None
|
|
form_caption = None
|
|
instance = None # The data holding model object
|
|
request = None
|
|
form_attrs = {} # Holds additional attributes, that can be used in the template
|
|
has_required_fields = False # Automatically set. Triggers hint rendering in templates
|
|
show_cancel_btn = True
|
|
label_input_ratio = (3, 9) # used for col-sm-xy in the template. Must sum up to 12. Specify on inheriting forms
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.instance = kwargs.pop("instance", None)
|
|
super().__init__(*args, **kwargs)
|
|
if self.request is not None:
|
|
self.user = self.request.user
|
|
# Check for required fields
|
|
for _field_name, _field_val in self.fields.items():
|
|
if _field_val.required:
|
|
self.has_required_fields = True
|
|
break
|
|
|
|
self.check_for_recorded_instance()
|
|
self.__check_valid_label_input_ratio()
|
|
|
|
@abstractmethod
|
|
def save(self):
|
|
# To be implemented in subclasses!
|
|
pass
|
|
|
|
def __check_valid_label_input_ratio(self):
|
|
""" Checks whether the configured label-input ratio is valid
|
|
|
|
If not valid an AssertionError will be raised.
|
|
The valid sum of label-input ratio is defined by bootstrap's column layout system.
|
|
|
|
Returns:
|
|
|
|
"""
|
|
ratio = self.label_input_ratio[0] + self.label_input_ratio[1]
|
|
if ratio != 12:
|
|
raise AssertionError(f"Label-input ratio on form must sum up to 12! It's {self.label_input_ratio}")
|
|
|
|
def disable_form_field(self, field: str):
|
|
"""
|
|
Disables a form field for user editing
|
|
"""
|
|
self.fields[field].widget.attrs["readonly"] = True
|
|
self.fields[field].disabled = True
|
|
self.fields[field].widget.attrs["title"] = _("Not editable")
|
|
|
|
def initialize_form_field(self, field: str, val):
|
|
"""
|
|
Initializes a form field with a value
|
|
"""
|
|
self.fields[field].initial = val
|
|
|
|
def add_placeholder_for_field(self, field: str, val):
|
|
"""
|
|
Adds a placeholder to a field after initialization without the need to redefine the form widget
|
|
|
|
Args:
|
|
field (str): Field name
|
|
val (str): Placeholder
|
|
|
|
Returns:
|
|
|
|
"""
|
|
self.fields[field].widget.attrs["placeholder"] = val
|
|
|
|
def load_initial_data(self, form_data: dict, disabled_fields: list = None):
|
|
""" Initializes form data from instance
|
|
|
|
Inserts instance data into form and disables form fields
|
|
|
|
Returns:
|
|
|
|
"""
|
|
if self.instance is None:
|
|
return
|
|
for k, v in form_data.items():
|
|
self.initialize_form_field(k, v)
|
|
if disabled_fields:
|
|
for field in disabled_fields:
|
|
self.disable_form_field(field)
|
|
|
|
def add_widget_html_class(self, field: str, cls: str):
|
|
""" Adds a HTML class string to the widget of a field
|
|
|
|
Args:
|
|
field (str): The field's name
|
|
cls (str): The new class string
|
|
|
|
Returns:
|
|
|
|
"""
|
|
set_class = self.fields[field].widget.attrs.get("class", "")
|
|
if cls in set_class:
|
|
return
|
|
else:
|
|
set_class += " " + cls
|
|
self.fields[field].widget.attrs["class"] = set_class
|
|
|
|
def remove_widget_html_class(self, field: str, cls: str):
|
|
""" Removes a HTML class string from the widget of a field
|
|
|
|
Args:
|
|
field (str): The field's name
|
|
cls (str): The new class string
|
|
|
|
Returns:
|
|
|
|
"""
|
|
set_class = self.fields[field].widget.attrs.get("class", "")
|
|
set_class = set_class.replace(cls, "")
|
|
self.fields[field].widget.attrs["class"] = set_class
|
|
|
|
def check_for_recorded_instance(self):
|
|
""" Checks if the instance is recorded and runs some special logic if yes
|
|
|
|
If the instance is recorded, the form shall not display any possibility to
|
|
edit any data. Instead, the users should get some information about why they can not edit anything.
|
|
|
|
There are situations where the form should be rendered regularly,
|
|
e.g deduction forms for (recorded) eco accounts.
|
|
|
|
Returns:
|
|
|
|
"""
|
|
is_none = self.instance is None
|
|
is_other_data_type = not isinstance(self.instance, BaseObject)
|
|
|
|
if is_none or is_other_data_type:
|
|
# Do nothing
|
|
return
|
|
|
|
if self.instance.is_recorded:
|
|
self.block_form()
|
|
|
|
def block_form(self):
|
|
"""
|
|
Overwrites template, providing no actions
|
|
|
|
Returns:
|
|
|
|
"""
|
|
self.template = "form/recorded_no_edit.html" |