0001-fields-extend-string-field-validation-support-11455.patch
tests/test_admin_pages.py | ||
---|---|---|
1300 | 1300 |
resp = resp.form.submit('submit') |
1301 | 1301 |
assert 'syntax error in Django template: Unexpected end of expression' in resp.body |
1302 | 1302 | |
1303 |
def test_form_edit_string_field_validation(pub): |
|
1304 |
create_superuser(pub) |
|
1305 |
create_role() |
|
1306 | ||
1307 |
FormDef.wipe() |
|
1308 |
formdef = FormDef() |
|
1309 |
formdef.name = 'form title' |
|
1310 |
formdef.fields = [fields.StringField(id='1', label='1st field', type='string')] |
|
1311 |
formdef.store() |
|
1312 | ||
1313 |
app = login(get_app(pub)) |
|
1314 |
resp = app.get('/backoffice/forms/1/') |
|
1315 |
resp = resp.click(href='fields/') |
|
1316 |
assert '1st field' in resp.body |
|
1317 | ||
1318 |
resp = resp.click('Edit', href='1/') |
|
1319 |
resp.form['validation$type'] = 'Regular Expression' |
|
1320 |
resp.form['validation$value_regex'] = r'\d+' |
|
1321 |
resp = resp.form.submit('submit').follow() |
|
1322 |
assert FormDef.get(formdef.id).fields[0].validation == {'type': 'regex', 'value': r'\d+'} |
|
1323 | ||
1324 |
resp = resp.click('Edit', href='1/') |
|
1325 |
resp.form['validation$type'] = 'Django Condition' |
|
1326 |
resp.form['validation$value_django'] = 'value|decimal < 20' |
|
1327 |
resp = resp.form.submit('submit').follow() |
|
1328 |
assert FormDef.get(formdef.id).fields[0].validation == {'type': 'django', 'value': 'value|decimal < 20'} |
|
1329 | ||
1330 |
resp = resp.click('Edit', href='1/') |
|
1331 |
resp.form['validation$type'] = 'Django Condition' |
|
1332 |
resp.form['validation$value_django'] = '{{ value|decimal < 20 }}' |
|
1333 |
resp = resp.form.submit('submit') |
|
1334 |
assert 'syntax error' in resp.body |
|
1335 | ||
1303 | 1336 |
def test_form_edit_item_field(pub): |
1304 | 1337 |
create_superuser(pub) |
1305 | 1338 |
create_role() |
tests/test_form_pages.py | ||
---|---|---|
4076 | 4076 |
def test_form_string_regex_field_submit(pub): |
4077 | 4077 |
formdef = create_formdef() |
4078 | 4078 |
formdef.fields = [fields.StringField(id='0', label='string', type='string', |
4079 |
validation=r'\d{5}$', required=False)]
|
|
4079 |
validation={'type': 'regex', 'value': r'\d{5}$'}, required=False)]
|
|
4080 | 4080 |
formdef.store() |
4081 | 4081 | |
4082 | 4082 |
formdef.data_class().wipe() |
... | ... | |
4112 | 4112 |
resp = get_app(pub).get('/test/') |
4113 | 4113 |
resp.forms[0]['f0'] = 'foobar' |
4114 | 4114 |
resp = resp.forms[0].submit('submit') |
4115 |
assert 'wrong format' in resp.body
|
|
4115 |
assert 'invalid value' in resp.body
|
|
4116 | 4116 | |
4117 | 4117 |
def test_form_text_field_submit(pub): |
4118 | 4118 |
formdef = create_formdef() |
tests/test_widgets.py | ||
---|---|---|
567 | 567 |
assert not widget.has_error() |
568 | 568 |
assert widget.parse() == 'bar' |
569 | 569 | |
570 |
def test_wcsextrastringwidget_regex_validation(): |
|
570 | 571 |
# check regex validation |
571 | 572 |
class FakeField: pass |
572 | 573 |
fakefield = FakeField() |
573 |
fakefield.validation = r'\d+'
|
|
574 |
fakefield.validation = {'type': 'regex', 'value': r'\d+'}
|
|
574 | 575 | |
575 | 576 |
widget = WcsExtraStringWidget('test', value='foo', required=False) |
576 | 577 |
widget.field = fakefield |
... | ... | |
587 | 588 |
mock_form_submission(req, widget, {'test': 'cdab 12'}) |
588 | 589 |
assert widget.has_error() |
589 | 590 | |
590 |
fakefield.validation = r'\d+(\.\d{1,2})?'
|
|
591 |
fakefield.validation = {'type': 'regex', 'value': r'\d+(\.\d{1,2})?'}
|
|
591 | 592 |
widget = WcsExtraStringWidget('test', value='foo', required=False) |
592 | 593 |
widget.field = fakefield |
593 | 594 |
mock_form_submission(req, widget, {'test': '12'}) |
... | ... | |
603 | 604 |
mock_form_submission(req, widget, {'test': '12,34'}) |
604 | 605 |
assert widget.has_error() |
605 | 606 | |
607 |
def test_wcsextrastringwidget_builtin_validation(): |
|
608 |
class FakeField: pass |
|
609 |
fakefield = FakeField() |
|
610 | ||
611 |
fakefield.validation = {'type': 'digits'} |
|
612 |
widget = WcsExtraStringWidget('test', value='foo', required=False) |
|
613 |
widget.field = fakefield |
|
614 |
mock_form_submission(req, widget, {'test': '12'}) |
|
615 |
assert not widget.has_error() |
|
616 | ||
617 |
widget = WcsExtraStringWidget('test', value='foo', required=False) |
|
618 |
widget.field = fakefield |
|
619 |
mock_form_submission(req, widget, {'test': 'az'}) |
|
620 |
assert widget.has_error() |
|
621 | ||
622 |
fakefield.validation = {'type': 'zipcode-fr'} |
|
623 |
widget = WcsExtraStringWidget('test', value='foo', required=False) |
|
624 |
widget.field = fakefield |
|
625 |
mock_form_submission(req, widget, {'test': '12345'}) |
|
626 |
assert not widget.has_error() |
|
627 | ||
628 |
widget = WcsExtraStringWidget('test', value='foo', required=False) |
|
629 |
widget.field = fakefield |
|
630 |
mock_form_submission(req, widget, {'test': '1234'}) |
|
631 |
assert widget.has_error() |
|
632 | ||
633 |
def test_wcsextrastringwidget_django_validation(): |
|
634 |
class FakeField: pass |
|
635 |
fakefield = FakeField() |
|
636 | ||
637 |
fakefield.validation = {'type': 'django', 'value': 'value|decimal and value|decimal < 20'} |
|
638 |
widget = WcsExtraStringWidget('test', value='foo', required=False) |
|
639 |
widget.field = fakefield |
|
640 |
mock_form_submission(req, widget, {'test': '12'}) |
|
641 |
assert not widget.has_error() |
|
642 | ||
643 |
widget = WcsExtraStringWidget('test', value='foo', required=False) |
|
644 |
widget.field = fakefield |
|
645 |
mock_form_submission(req, widget, {'test': '35'}) |
|
646 |
assert widget.has_error() |
|
647 | ||
648 |
widget = WcsExtraStringWidget('test', value='foo', required=False) |
|
649 |
widget.field = fakefield |
|
650 |
mock_form_submission(req, widget, {'test': 'az'}) |
|
651 |
assert widget.has_error() |
|
652 | ||
606 | 653 | |
607 | 654 |
def test_widgetdict_widget(): |
608 | 655 |
widget = WidgetDict('test', value={'a': None, 'b': None, 'c': None}) |
wcs/fields.py | ||
---|---|---|
691 | 691 |
value=self.size) |
692 | 692 |
else: |
693 | 693 |
form.add(HiddenWidget, 'size', value=None) |
694 |
form.add(RegexStringWidget, 'validation', title = _('Validation regex'),
|
|
695 |
value=self.validation, advanced=(not self.validation)) |
|
694 |
form.add(ValidationWidget, 'validation', title=_('Validation'),
|
|
695 |
value=self.validation, advanced=(not self.validation))
|
|
696 | 696 |
form.add(data_sources.DataSourceSelectionWidget, 'data_source', |
697 | 697 |
value=self.data_source, |
698 | 698 |
title=_('Data Source'), |
... | ... | |
720 | 720 |
return None |
721 | 721 |
return str(value) |
722 | 722 | |
723 |
def migrate(self): |
|
724 |
if isinstance(self.validation, basestring): |
|
725 |
self.validation = {'type': 'regex', 'value': self.validation} |
|
726 |
return True |
|
727 |
return False |
|
728 | ||
729 |
def init_with_xml(self, element, charset, include_id=False): |
|
730 |
super(StringField, self).init_with_xml(element, charset, include_id=include_id) |
|
731 |
self.migrate() |
|
732 | ||
723 | 733 |
register_field_class(StringField) |
724 | 734 | |
725 | 735 |
wcs/qommon/form.py | ||
---|---|---|
892 | 892 |
pass |
893 | 893 | |
894 | 894 | |
895 |
class ValidationCondition(Condition): |
|
896 |
def __init__(self, django_condition, value): |
|
897 |
super(ValidationCondition, self).__init__({'type': 'django', 'value': django_condition}) |
|
898 |
self.evaluated_value = value |
|
899 | ||
900 |
def get_data(self): |
|
901 |
return {'value': self.evaluated_value} |
|
902 | ||
903 | ||
904 |
class ValidationWidget(CompositeWidget): |
|
905 |
validation_methods = collections.OrderedDict([ |
|
906 |
('digits', {'title': N_('Digits'), 'regex': '\d+'}), |
|
907 |
('phone-fr', {'title': N_('Phone Number (France)'), 'regex': '0[\d\.\s]{9}'}), |
|
908 |
('zipcode-fr', {'title': N_('Zip Code (France)'), 'regex': '\d{5}'}), |
|
909 |
('regex', {'title': N_('Regular Expression')}), |
|
910 |
('django', {'title': N_('Django Condition')}), |
|
911 |
]) |
|
912 | ||
913 |
def __init__(self, name, value=None, **kwargs): |
|
914 |
super(ValidationWidget, self).__init__(name, value=value, **kwargs) |
|
915 |
if not value: |
|
916 |
value = {} |
|
917 | ||
918 |
options = [('none', _('None'))] + [(x, _(y['title'])) for x, y in self.validation_methods.items()] |
|
919 | ||
920 |
self.add(SingleSelectWidget, 'type', options=options, value=value.get('type'), |
|
921 |
attrs={'data-dynamic-display-parent': 'true'}) |
|
922 |
self.parse() |
|
923 |
if not self.value: |
|
924 |
self.value = {} |
|
925 | ||
926 |
validation_labels = collections.OrderedDict(options) |
|
927 |
self.add(RegexStringWidget, 'value_regex', size=80, |
|
928 |
value=value.get('value') if value.get('type') == 'regex' else None, |
|
929 |
attrs={'data-dynamic-display-child-of': 'validation$type', |
|
930 |
'data-dynamic-display-value': validation_labels.get('regex')}) |
|
931 |
self.add(DjangoConditionWidget, 'value_django', size=80, |
|
932 |
value=value.get('value') if value.get('type') == 'django' else None, |
|
933 |
attrs={'data-dynamic-display-child-of': 'validation$type', |
|
934 |
'data-dynamic-display-value': validation_labels.get('django')}) |
|
935 |
self._parsed = False |
|
936 | ||
937 |
def _parse(self, request): |
|
938 |
values = {} |
|
939 |
type_ = self.get('type') |
|
940 |
if type_: |
|
941 |
values['type'] = type_ |
|
942 |
value = self.get('value_%s' % type_) |
|
943 |
if value: |
|
944 |
values['value'] = value |
|
945 |
self.value = values or None |
|
946 | ||
947 |
def render_content(self): |
|
948 |
r = TemplateIO(html=True) |
|
949 |
for widget in self.get_widgets(): |
|
950 |
r += widget.render_error(widget.get_error()) |
|
951 |
for widget in self.get_widgets(): |
|
952 |
r += widget.render_content() |
|
953 |
return r.getvalue() |
|
954 | ||
955 |
@classmethod |
|
956 |
def get_validation_function(cls, validation): |
|
957 |
pattern = cls.get_validation_pattern(validation) |
|
958 |
if pattern: |
|
959 |
def regex_validation(value): |
|
960 |
match = re.match(pattern, value) |
|
961 |
return bool(match and match.group() == value) |
|
962 |
return regex_validation |
|
963 |
if validation['type'] == 'django': |
|
964 |
def django_validation(value): |
|
965 |
condition = ValidationCondition(validation['value'], value=value) |
|
966 |
return condition.evaluate() |
|
967 |
return django_validation |
|
968 | ||
969 |
@classmethod |
|
970 |
def get_validation_pattern(cls, validation): |
|
971 |
validation_method = cls.validation_methods.get(validation['type']) |
|
972 |
if validation_method and validation_method.get('regex'): |
|
973 |
return validation_method.get('regex') |
|
974 |
if validation['type'] == 'regex': |
|
975 |
return validation['value'] |
|
976 |
return None |
|
977 | ||
978 | ||
895 | 979 |
class WcsExtraStringWidget(StringWidget): |
896 | 980 |
field = None |
897 | 981 |
prefill = False |
... | ... | |
903 | 987 | |
904 | 988 |
def render_content(self): |
905 | 989 |
if self.field and self.field.validation and not 'pattern' in self.attrs: |
906 |
self.attrs['pattern'] = self.field.validation |
|
907 |
s = StringWidget.render_content(self) |
|
908 |
return s |
|
990 |
pattern = ValidationWidget.get_validation_pattern(self.field.validation) |
|
991 |
if pattern: |
|
992 |
self.attrs['pattern'] = pattern |
|
993 |
return super(WcsExtraStringWidget, self).render_content() |
|
909 | 994 | |
910 | 995 |
def _parse(self, request): |
911 | 996 |
StringWidget._parse(self, request) |
912 | 997 |
if self.field and self.field.validation and self.value is not None: |
913 |
match = re.match(self.field.validation, self.value)
|
|
914 |
if not match or match.group() != self.value:
|
|
915 |
self.error = _('wrong format')
|
|
998 |
validation_function = ValidationWidget.get_validation_function(self.field.validation)
|
|
999 |
if not validation_function(self.value):
|
|
1000 |
self.error = _('invalid value')
|
|
916 | 1001 | |
917 | 1002 | |
918 | 1003 |
class DateWidget(StringWidget): |
... | ... | |
2348 | 2433 |
'value_python': self.get_widget('value_python').render_content(), |
2349 | 2434 |
'type': self.get_widget('type').render_content() |
2350 | 2435 |
} |
2436 | ||
2437 | ||
2438 |
class DjangoConditionWidget(StringWidget): |
|
2439 |
def _parse(self, request): |
|
2440 |
super(DjangoConditionWidget, self)._parse(request) |
|
2441 |
if self.value: |
|
2442 |
try: |
|
2443 |
Condition({'type': 'django', 'value': self.value}).validate() |
|
2444 |
except ValidationError as e: |
|
2445 |
self.set_error(str(e)) |
|
2351 |
- |