Projet

Général

Profil

0001-fields-custom-error-message-for-django-regex-validat.patch

Lauréline Guérin, 04 février 2021 15:13

Télécharger (12,5 ko)

Voir les différences:

Subject: [PATCH] fields: custom error message for django/regex validation
 (#50772)

 tests/admin_pages/test_form.py | 14 ++++++-
 tests/test_widgets.py          | 32 +++++++++++++++
 wcs/qommon/form.py             | 75 ++++++++++++++++++++++++++++++----
 3 files changed, 112 insertions(+), 9 deletions(-)
tests/admin_pages/test_form.py
1501 1501
    resp = resp.click('Edit', href='1/')
1502 1502
    resp.form['validation$type'] = 'Regular Expression'
1503 1503
    resp.form['validation$value_regex'] = r'\d+'
1504
    resp.form['validation$error_message'] = 'Foo Error'
1504 1505
    resp = resp.form.submit('submit').follow()
1505
    assert FormDef.get(formdef.id).fields[0].validation == {'type': 'regex', 'value': r'\d+'}
1506
    assert FormDef.get(formdef.id).fields[0].validation == {
1507
        'type': 'regex',
1508
        'value': r'\d+',
1509
        'error_message': 'Foo Error',
1510
    }
1506 1511

  
1507 1512
    resp = resp.click('Edit', href='1/')
1508 1513
    resp.form['validation$type'] = 'None'
......
1512 1517
    resp = resp.click('Edit', href='1/')
1513 1518
    resp.form['validation$type'] = 'Django Condition'
1514 1519
    resp.form['validation$value_django'] = 'value|decimal < 20'
1520
    resp.form['validation$error_message'] = 'Bar Error'
1515 1521
    resp = resp.form.submit('submit').follow()
1516
    assert FormDef.get(formdef.id).fields[0].validation == {'type': 'django', 'value': 'value|decimal < 20'}
1522
    assert FormDef.get(formdef.id).fields[0].validation == {
1523
        'type': 'django',
1524
        'value': 'value|decimal < 20',
1525
        'error_message': 'Bar Error',
1526
    }
1517 1527

  
1518 1528
    resp = resp.click('Edit', href='1/')
1519 1529
    resp.form['validation$type'] = 'Django Condition'
tests/test_widgets.py
743 743
    widget.field = fakefield
744 744
    mock_form_submission(req, widget, {'test': '12,34'})
745 745
    assert widget.has_error()
746
    assert widget.error == 'invalid value'
747

  
748
    widget = WcsExtraStringWidget('test', value='foo', required=False)
749
    fakefield.validation = {
750
        'type': 'regex',
751
        'value': r'\d+(\.\d{1,2})?',
752
        'error_message': 'Foo Bar Custom Error',
753
    }
754
    widget.field = fakefield
755
    mock_form_submission(req, widget, {'test': '12,34'})
756
    assert widget.has_error()
757
    assert widget.error == 'Foo Bar Custom Error'
746 758

  
747 759

  
748 760
def test_wcsextrastringwidget_builtin_validation():
......
761 773
    widget.field = fakefield
762 774
    mock_form_submission(req, widget, {'test': 'az'})
763 775
    assert widget.has_error()
776
    assert widget.error == 'Only digits are allowed'
764 777

  
765 778
    fakefield.validation = {'type': 'zipcode-fr'}
766 779
    widget = WcsExtraStringWidget('test', value='foo', required=False)
......
777 790
    widget.field = fakefield
778 791
    mock_form_submission(req, widget, {'test': '1234'})
779 792
    assert widget.has_error()
793
    assert widget.error == 'Invalid zip code'
780 794

  
781 795

  
782 796
def test_wcsextrastringwidget_phone():
......
798 812
        widget.field = fakefield
799 813
        mock_form_submission(req, widget, {'test': invalid_case})
800 814
        assert widget.has_error()
815
        assert widget.error == 'Invalid phone number'
801 816

  
802 817
    # and check it gets a special HTML input type
803 818
    widget = WcsExtraStringWidget('test', value='foo', required=False)
......
827 842
    widget.field = fakefield
828 843
    mock_form_submission(req, widget, {'test': 'az'})
829 844
    assert widget.has_error()
845
    assert widget.error == 'Invalid phone number'
830 846

  
831 847
    widget = WcsExtraStringWidget('test', value='foo', required=False)
832 848
    widget.field = fakefield
......
858 874
    widget.field = fakefield
859 875
    mock_form_submission(req, widget, {'test': '443170130'})
860 876
    assert widget.has_error()
877
    assert widget.error == 'Invalid SIREN code'
861 878

  
862 879
    widget = WcsExtraStringWidget('test', value='foo', required=False)
863 880
    widget.field = fakefield
......
899 916
    widget.field = fakefield
900 917
    mock_form_submission(req, widget, {'test': '44317013900037'})
901 918
    assert widget.has_error()
919
    assert widget.error == 'Invalid SIRET code'
902 920

  
903 921
    widget = WcsExtraStringWidget('test', value='foo', required=False)
904 922
    widget.field = fakefield
......
940 958
    widget.field = fakefield
941 959
    mock_form_submission(req, widget, {'test': '42'})
942 960
    assert widget.has_error()
961
    assert widget.error == 'Invalid NIR'
943 962

  
944 963
    widget = WcsExtraStringWidget('test', value='foo', required=False)
945 964
    widget.field = fakefield
......
1016 1035
        widget.field = fakefield
1017 1036
        mock_form_submission(req, widget, {'test': iban.replace(' ', '')})
1018 1037
        assert widget.has_error()
1038
        assert widget.error == 'Invalid IBAN'
1019 1039

  
1020 1040

  
1021 1041
def test_wcsextrastringwidget_django_validation():
......
1039 1059
    widget.field = fakefield
1040 1060
    mock_form_submission(req, widget, {'test': 'az'})
1041 1061
    assert widget.has_error()
1062
    assert widget.error == 'invalid value'
1063

  
1064
    widget = WcsExtraStringWidget('test', value='foo', required=False)
1065
    fakefield.validation = {
1066
        'type': 'django',
1067
        'value': 'value|decimal and value|decimal < 20',
1068
        'error_message': 'Foo Bar Custom Error',
1069
    }
1070
    widget.field = fakefield
1071
    mock_form_submission(req, widget, {'test': 'az'})
1072
    assert widget.has_error()
1073
    assert widget.error == 'Foo Bar Custom Error'
1042 1074

  
1043 1075

  
1044 1076
def test_widgetdict_widget():
wcs/qommon/form.py
1051 1051
class ValidationWidget(CompositeWidget):
1052 1052
    validation_methods = collections.OrderedDict(
1053 1053
        [
1054
            ('digits', {'title': N_('Digits'), 'regex': r'\d+', 'html_inputmode': 'numeric'}),
1054
            (
1055
                'digits',
1056
                {
1057
                    'title': N_('Digits'),
1058
                    'regex': r'\d+',
1059
                    'error_message': N_('Only digits are allowed'),
1060
                    'html_inputmode': 'numeric',
1061
                },
1062
            ),
1055 1063
            (
1056 1064
                'phone',
1057
                {'title': N_('Phone Number'), 'regex': r'\+?[-\(\)\d\.\s/]+', 'html_input_type': 'tel'},
1065
                {
1066
                    'title': N_('Phone Number'),
1067
                    'regex': r'\+?[-\(\)\d\.\s/]+',
1068
                    'error_message': N_('Invalid phone number'),
1069
                    'html_input_type': 'tel',
1070
                },
1058 1071
            ),
1059 1072
            (
1060 1073
                'phone-fr',
1061 1074
                {
1062 1075
                    'title': N_('Phone Number (France)'),
1063 1076
                    'function': 'validate_phone_fr',
1077
                    'error_message': N_('Invalid phone number'),
1064 1078
                    'html_input_type': 'tel',
1065 1079
                },
1066 1080
            ),
1067 1081
            (
1068 1082
                'zipcode-fr',
1069
                {'title': N_('Zip Code (France)'), 'regex': r'\d{5}', 'html_inputmode': 'numeric'},
1083
                {
1084
                    'title': N_('Zip Code (France)'),
1085
                    'regex': r'\d{5}',
1086
                    'error_message': N_('Invalid zip code'),
1087
                    'html_inputmode': 'numeric',
1088
                },
1070 1089
            ),
1071 1090
            (
1072 1091
                'siren-fr',
1073 1092
                {
1074 1093
                    'title': N_('SIREN Code (France)'),
1075 1094
                    'function': 'validate_siren',
1095
                    'error_message': N_('Invalid SIREN code'),
1076 1096
                    'html_inputmode': 'numeric',
1077 1097
                },
1078 1098
            ),
......
1081 1101
                {
1082 1102
                    'title': N_('SIRET Code (France)'),
1083 1103
                    'function': 'validate_siret',
1104
                    'error_message': N_('Invalid SIRET code'),
1084 1105
                    'html_inputmode': 'numeric',
1085 1106
                },
1086 1107
            ),
1087
            ('nir-fr', {'title': N_('NIR (France)'), 'function': 'validate_nir'}),
1088
            ('iban', {'title': N_('IBAN'), 'function': 'validate_iban'}),
1108
            (
1109
                'nir-fr',
1110
                {'title': N_('NIR (France)'), 'error_message': N_('Invalid NIR'), 'function': 'validate_nir'},
1111
            ),
1112
            ('iban', {'title': N_('IBAN'), 'function': 'validate_iban', 'error_message': N_('Invalid IBAN')}),
1089 1113
            ('regex', {'title': N_('Regular Expression')}),
1090 1114
            ('django', {'title': N_('Django Condition')}),
1091 1115
        ]
......
1130 1154
                'data-dynamic-display-value': validation_labels.get('django'),
1131 1155
            },
1132 1156
        )
1157
        self.add(
1158
            StringWidget,
1159
            'error_message',
1160
            size=80,
1161
            value=value.get('error_message') if value.get('type') in ['regex', 'django'] else None,
1162
            title=_('Custom error message'),
1163
            hint=_('This message will be be displayed if validation fails.'),
1164
            attrs={
1165
                'data-dynamic-display-child-of': 'validation$type',
1166
                'data-dynamic-display-value-in': '|'.join(
1167
                    [validation_labels.get('regex'), validation_labels.get('django')]
1168
                ),
1169
            },
1170
        )
1133 1171
        self._parsed = False
1134 1172

  
1135 1173
    def _parse(self, request):
......
1140 1178
            value = self.get('value_%s' % type_)
1141 1179
            if value:
1142 1180
                values['value'] = value
1181
            if type_ in ['regex', 'django']:
1182
                error_message = self.get('error_message')
1183
                if error_message:
1184
                    values['error_message'] = error_message
1143 1185
        self.value = values or None
1144 1186

  
1145 1187
    def render_content(self):
1146 1188
        r = TemplateIO(html=True)
1147
        for widget in self.get_widgets():
1189
        inlines = ['type', 'value_regex', 'value_django']
1190
        for name in inlines:
1191
            widget = self.get_widget(name)
1148 1192
            r += widget.render_error(widget.get_error())
1149
        for widget in self.get_widgets():
1193
        for name in inlines:
1194
            widget = self.get_widget(name)
1150 1195
            r += widget.render_content()
1196
        widget = self.get_widget('error_message')
1197
        r += widget.render()
1151 1198
        return r.getvalue()
1152 1199

  
1153 1200
    @classmethod
......
1170 1217
        if validation_method and 'function' in validation_method:
1171 1218
            return getattr(misc, validation_method['function'])
1172 1219

  
1220
    @classmethod
1221
    def get_validation_error_message(cls, validation):
1222
        pattern = cls.get_validation_pattern(validation)
1223
        if validation['type'] == 'regex' and pattern:
1224
            return validation.get('error_message')
1225
        if validation['type'] == 'django' and validation.get('value'):
1226
            return validation.get('error_message')
1227
        validation_method = cls.validation_methods.get(validation['type'])
1228
        if validation_method and 'error_message' in validation_method:
1229
            return validation_method['error_message']
1230

  
1173 1231
    @classmethod
1174 1232
    def get_validation_pattern(cls, validation):
1175 1233
        validation_method = cls.validation_methods.get(validation['type'])
......
1214 1272
        StringWidget._parse(self, request)
1215 1273
        if self.field and self.field.validation and self.value is not None:
1216 1274
            self.validation_function = ValidationWidget.get_validation_function(self.field.validation)
1275
            self.validation_function_error_message = ValidationWidget.get_validation_error_message(
1276
                self.field.validation
1277
            )
1217 1278

  
1218 1279
        if self.validation_function and not self.validation_function(self.value):
1219 1280
            self.error = self.validation_function_error_message or _('invalid value')
1220
-