preserve user input in case of form validation errors

This commit is contained in:
Jakob Ketterl 2024-01-17 20:05:47 +01:00
parent 32aeebd7a3
commit 79e81295d5
4 changed files with 28 additions and 11 deletions

View file

@ -20,6 +20,7 @@ class SettingsFormController(AuthorizationMixin, BreadcrumbMixin, WebpageControl
super().__init__(handler, request, options)
self.errors = {}
self.globalError = None
self.formData = None
@abstractmethod
def getSections(self):
@ -35,8 +36,18 @@ class SettingsFormController(AuthorizationMixin, BreadcrumbMixin, WebpageControl
def getErrors(self):
return self.errors
def buildRenderData(self):
# this basially builds an intermediate result to be rendered
# relevant when the form has to be displayed again due to errors
# in this specific scenario, we mix the config with the data the user already submitted
# we use a copy of the config so that whatever we apply here does not get accidentally stored
res = self.getData().__dict__()
if self.formData is not None:
self._applyConfigData(res, self.formData)
return res
def render_sections(self):
sections = "".join(section.render(self.getData(), self.getErrors()) for section in self.getSections())
sections = "".join(section.render(self.buildRenderData(), self.getErrors()) for section in self.getSections())
buttons = self.render_buttons()
return """
<form class="settings-body" method="POST">
@ -97,6 +108,7 @@ class SettingsFormController(AuthorizationMixin, BreadcrumbMixin, WebpageControl
self.globalError = str(e)
return self.indexAction()
self.formData = data
if errors:
self.errors = self._mergeErrors(errors)
return self.indexAction()
@ -109,14 +121,17 @@ class SettingsFormController(AuthorizationMixin, BreadcrumbMixin, WebpageControl
self.globalError = str(e)
return self.indexAction()
def processData(self, data):
config = self.getData()
def _applyConfigData(self, dest, data):
for k, v in data.items():
if v is None:
if k in config:
del config[k]
if k in dest:
del dest[k]
else:
config[k] = v
dest[k] = v
def processData(self, data):
config = self.getData()
self._applyConfigData(config, data)
def store(self):
Config.get().store()

View file

@ -91,11 +91,13 @@ class Input(ABC):
def parse(self, data):
if self.id in data:
value = self.converter.convert_from_form(data[self.id][0])
if self.validator is not None:
self.validator.validate(self.id, value)
return {self.id: value}
return {}
def validate(self, data):
if self.id in data and self.validator is not None:
self.validator.validate(self.id, data[self.id])
def getLabel(self):
return self.label

View file

@ -59,6 +59,4 @@ class LocationInput(Input):
def parse(self, data):
value = {k: float(data["{0}-{1}".format(self.id, k)][0]) for k in ["lat", "lon"]}
if self.validator is not None:
self.validator.validate(self.id, value)
return {self.id: value}

View file

@ -34,7 +34,9 @@ class Section(object):
errors = []
for i in self.inputs:
try:
parsed_data.update(i.parse(data))
res = i.parse(data)
parsed_data.update(res)
i.validate(res)
except FormError as e:
errors.append(e)
except Exception as e: