misc: extend custom error messages to all validation types (#63038) #216

Merged
fpeters merged 1 commits from wip/63038-custom-error-messages into main 2023-04-03 09:22:36 +02:00
3 changed files with 58 additions and 20 deletions

View File

@ -2008,7 +2008,7 @@ def test_form_edit_string_field_validation(pub):
assert '1st field' in resp.text
resp = resp.click('Edit', href='1/')
resp.form['validation$type'] = 'Regular Expression'
resp.form['validation$type'] = 'regex'
resp.form['validation$value_regex'] = r'\d+'
resp.form['validation$error_message'] = 'Foo Error'
resp = resp.form.submit('submit').follow()
@ -2019,12 +2019,12 @@ def test_form_edit_string_field_validation(pub):
}
resp = resp.click('Edit', href='1/')
resp.form['validation$type'] = 'None'
resp.form['validation$type'] = ''
resp = resp.form.submit('submit').follow()
assert FormDef.get(formdef.id).fields[0].validation is None
resp = resp.click('Edit', href='1/')
resp.form['validation$type'] = 'Django Condition'
resp.form['validation$type'] = 'django'
resp.form['validation$value_django'] = 'value|decimal < 20'
resp.form['validation$error_message'] = 'Bar Error'
resp = resp.form.submit('submit').follow()
@ -2035,11 +2035,30 @@ def test_form_edit_string_field_validation(pub):
}
resp = resp.click('Edit', href='1/')
resp.form['validation$type'] = 'Django Condition'
resp.form['validation$type'] = 'django'
resp.form['validation$value_django'] = '{{ value|decimal < 20 }}'
resp = resp.form.submit('submit')
assert 'syntax error' in resp.text
# check default error message is not saved
resp.form['validation$value_django'] = ''
resp.form['validation$type'] = 'time'
resp.form['validation$error_message'] = 'Invalid time'
resp = resp.form.submit('submit').follow()
assert FormDef.get(formdef.id).fields[0].validation == {
'type': 'time',
}
# but custom message is saved
resp = resp.click('Edit', href='1/')
resp.form['validation$type'] = 'time'
resp.form['validation$error_message'] = 'Invalid time, it must be in hh:mm format.'
resp = resp.form.submit('submit')
assert FormDef.get(formdef.id).fields[0].validation == {
'type': 'time',
'error_message': 'Invalid time, it must be in hh:mm format.',
}
def test_form_edit_text_field(pub):
create_superuser(pub)

View File

@ -1218,7 +1218,7 @@ class ValidationWidget(CompositeWidget):
if not value:
value = {}
options = [(None, _('None'))] + [(x, y['title']) for x, y in self.validation_methods.items()]
options = [(None, _('None'), '')] + [(x, y['title'], x) for x, y in self.validation_methods.items()]
Review

C'est ça qui fait les changements dans les tests type

-    resp.form['validation$type'] = 'Regular Expression'
+    resp.form['validation$type'] = 'regex' 

et plus bas

-                'data-dynamic-display-value': validation_labels.get('regex'),
+                'data-dynamic-display-value': 'regex', 
C'est ça qui fait les changements dans les tests type ``` - resp.form['validation$type'] = 'Regular Expression' + resp.form['validation$type'] = 'regex' ``` et plus bas ``` - 'data-dynamic-display-value': validation_labels.get('regex'), + 'data-dynamic-display-value': 'regex', ```
self.add(
SingleSelectWidget,
@ -1231,7 +1231,6 @@ class ValidationWidget(CompositeWidget):
if not self.value:
self.value = {}
validation_labels = collections.OrderedDict(options)
self.add(
RegexStringWidget,
'value_regex',
@ -1239,7 +1238,7 @@ class ValidationWidget(CompositeWidget):
value=value.get('value') if value.get('type') == 'regex' else None,
attrs={
'data-dynamic-display-child-of': 'validation$type',
'data-dynamic-display-value': validation_labels.get('regex'),
'data-dynamic-display-value': 'regex',
},
)
self.add(
@ -1249,21 +1248,22 @@ class ValidationWidget(CompositeWidget):
value=value.get('value') if value.get('type') == 'django' else None,
attrs={
'data-dynamic-display-child-of': 'validation$type',
'data-dynamic-display-value': validation_labels.get('django'),
'data-dynamic-display-value': 'django',
},
)
self.add(
StringWidget,
'error_message',
size=80,
value=value.get('error_message') if value.get('type') in ['regex', 'django'] else None,
value=value.get('error_message') if value.get('type') else None,
title=_('Custom error message'),
hint=_('This message will be be displayed if validation fails.'),
hint=_(
'This message will be be displayed if validation fails. '
'An empty value will give the default error message.'
),
attrs={
'data-dynamic-display-child-of': 'validation$type',
'data-dynamic-display-value-in': '|'.join(
[str(validation_labels.get('regex')), str(validation_labels.get('django'))]
),
'data-dynamic-display-value-in': '|'.join([x[2] for x in options if x[2]]),
Review

Le code précédent était pour prendre les libellés des options regex et django, le nouveau code est pour prendre tous les identifiants de type de validation, vu qu'on veut désormais le champ de message perso pour tous les types de validation.

Le code précédent était pour prendre les libellés des options regex et django, le nouveau code est pour prendre tous les identifiants de type de validation, vu qu'on veut désormais le champ de message perso pour tous les types de validation.
},
)
self._parsed = False
@ -1276,9 +1276,10 @@ class ValidationWidget(CompositeWidget):
value = self.get('value_%s' % type_)
if value:
values['value'] = value
if type_ in ['regex', 'django']:
error_message = self.get('error_message')
if error_message:
error_message = self.get('error_message')
if error_message:
default_error_message = self.validation_methods[type_].get('error_message')
if error_message != default_error_message:
values['error_message'] = error_message
self.value = values or None
@ -1293,6 +1294,15 @@ class ValidationWidget(CompositeWidget):
r += widget.render_content()
widget = self.get_widget('error_message')
r += widget.render()
error_messages = {
x: str(y.get('error_message'))
for x, y in self.validation_methods.items()
if y.get('error_message')
}
r += htmltext(
'<script id="validation-error-messages" type="application/json">%s</script>'
% json.dumps(error_messages)
)
Review

C'est équivalent au |json_script dispo en templatetag, c'est utilisé ensuite pour avoir tous les libellés disponibles dans le javascript.

C'est équivalent au |json_script dispo en templatetag, c'est utilisé ensuite pour avoir tous les libellés disponibles dans le javascript.
return r.getvalue()
@classmethod
@ -1317,10 +1327,7 @@ class ValidationWidget(CompositeWidget):
@classmethod
def get_validation_error_message(cls, validation):
pattern = cls.get_validation_pattern(validation)
if validation['type'] == 'regex' and pattern:
return validation.get('error_message')
if validation['type'] == 'django' and validation.get('value'):
if validation.get('error_message'):
Review

Il n'y avait vraiment pas de raison particulière à ne pas retourner le message personnalisé quand pattern n'était pas défini (en tout cas je n'ai rien trouvé qui aurait expliqué ça).

Il n'y avait vraiment pas de raison particulière à ne pas retourner le message personnalisé quand pattern n'était pas défini (en tout cas je n'ai rien trouvé qui aurait expliqué ça).
return validation.get('error_message')
validation_method = cls.validation_methods.get(validation['type'])
if validation_method and 'error_message' in validation_method:

View File

@ -350,6 +350,18 @@ $(function() {
});
$('[type=radio][name=display_mode]:checked').trigger('change');
if (document.getElementById('validation-error-messages')) {
const error_messages = JSON.parse(document.getElementById('validation-error-messages').textContent);
const error_message_widget = document.getElementById('form_validation__error_message');
$('#form_validation__type').on('change', function() {
var current_message = error_message_widget.value;
var new_message = error_messages[$(this).val()];
if (! current_message || Object.values(error_messages).indexOf(current_message) != -1) {
error_message_widget.value = new_message || '';
}
}).trigger('change');
}
Review

C'est encore un mix avec un peu de jquery, ça reste plus facile.

L'objectif ici est de remplir le champ "message personnalisé" avec le message par défaut, et tant que c'est un des messages par défaut, on peut le remplacer, sinon on laisse celui qui aurait été tapé/modifié par l'utilisateur.

C'est encore un mix avec un peu de jquery, ça reste plus facile. L'objectif ici est de remplir le champ "message personnalisé" avec le message par défaut, et tant que c'est un des messages par défaut, on peut le remplacer, sinon on laisse celui qui aurait été tapé/modifié par l'utilisateur.
function prepate_journal_links() {
$('#journal-page-links a').on('click', function() {
var url = $(this).attr('href');