Skip to content

Commit aa20ae8

Browse files
committed
Updated validator.
1 parent 5bd684a commit aa20ae8

File tree

2 files changed

+126
-55
lines changed

2 files changed

+126
-55
lines changed

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ increase-version: clean-build guard-PART ## Bump the project version (using the
6767
# ----------------------------------------------------------
6868
# --------- Run project Test -------------------------------
6969
# ----------------------------------------------------------
70+
test:
71+
@$(MANAGE_PY) test
72+
7073
tox: install-test ## Run tox test
7174
@tox
7275

extra_validator/field_validation/validator.py

Lines changed: 123 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import print_function
2+
13
import django
24
from django.core import validators
35
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
@@ -7,7 +9,7 @@
79
from django.utils.translation import ugettext as _
810
else:
911
from django.utils.translation import gettext as _
10-
12+
1113

1214
def field_to_str(fields):
1315
return _(
@@ -38,12 +40,12 @@ class FieldValidationMixin:
3840
# If condition is True ensure none of the fields are provided
3941
CONDITIONAL_REQUIRED_EMPTY_FIELDS = []
4042

41-
ERROR_HANDLERS = {'form': ValidationError}
43+
ERROR_HANDLERS = {"form": ValidationError}
4244

4345
EMPTY_VALUES = list(validators.EMPTY_VALUES)
4446

4547
@staticmethod
46-
def _error_as_dict(field, msg, code='required', error_class=ValidationError):
48+
def _error_as_dict(field, msg, code="required", error_class=ValidationError):
4749
return {field: error_class(_(msg), code=code)}
4850

4951
def _validate_only_one_option(self, selections, found_keys):
@@ -54,27 +56,40 @@ def _validate_only_one_option(self, selections, found_keys):
5456
if key in section:
5557
provided_fields.append(key)
5658
if len(set(provided_fields)) > 1:
57-
msg = f'Please provide only one of: {field_to_str(section)}'
58-
error_dict.update(self._error_as_dict(provided_fields[-1], msg, code='invalid'))
59+
msg = "Please provide only one of: {fields}".format(
60+
fields=field_to_str(section)
61+
)
62+
error_dict.update(
63+
self._error_as_dict(provided_fields[-1], msg, code="invalid")
64+
)
5965
break
6066
return error_dict
6167

6268
def _clean_conditional_toggle_fields(self, exclude=None, context=None):
6369
if self.CONDITIONAL_REQUIRED_TOGGLE_FIELDS:
6470
return self._clean_conditional_fields(
65-
exclude, context, field_sets=self.CONDITIONAL_REQUIRED_TOGGLE_FIELDS, validate_one=True,
71+
exclude,
72+
context,
73+
field_sets=self.CONDITIONAL_REQUIRED_TOGGLE_FIELDS,
74+
validate_one=True,
6675
)
6776

6877
def _clean_conditional_min_fields(self, exclude=None, context=None):
6978
if self.CONDITIONAL_REQUIRED_MIN_FIELDS:
7079
return self._clean_conditional_fields(
71-
exclude, context, field_sets=self.CONDITIONAL_REQUIRED_MIN_FIELDS, at_least_one=True,
80+
exclude,
81+
context,
82+
field_sets=self.CONDITIONAL_REQUIRED_MIN_FIELDS,
83+
at_least_one=True,
7284
)
7385

7486
def _clean_conditional_empty_fields(self, exclude=None, context=None):
7587
if self.CONDITIONAL_REQUIRED_EMPTY_FIELDS:
7688
return self._clean_conditional_fields(
77-
exclude, context, field_sets=self.CONDITIONAL_REQUIRED_EMPTY_FIELDS, none_provided=True,
89+
exclude,
90+
context,
91+
field_sets=self.CONDITIONAL_REQUIRED_EMPTY_FIELDS,
92+
none_provided=True,
7893
)
7994

8095
def _clean_conditional_fields(
@@ -96,7 +111,9 @@ def _clean_conditional_fields(
96111
for condition, fields in field_sets:
97112
field_names = list(filter(lambda f: f not in exclude, fields))
98113
if field_names:
99-
is_valid_condition = condition if isinstance(condition, bool) else False
114+
is_valid_condition = (
115+
condition if isinstance(condition, bool) else False
116+
)
100117
if callable(condition):
101118
is_valid_condition = condition(self)
102119

@@ -110,40 +127,62 @@ def _clean_conditional_fields(
110127
if not field_value_map and not none_provided:
111128
if len(fields) > 1:
112129
msg = (
113-
f'Please provide a valid value for the following fields: '
114-
f'{field_to_str(fields)}' if not validate_one else
115-
f'Please provide a valid value for any of the following fields: '
116-
f'{field_to_str(fields)}'
130+
"Please provide a valid value for the following fields: "
131+
"{fields}"
132+
if not validate_one
133+
else "Please provide a valid value for any of the following fields: "
134+
"{fields}".format(fields=field_to_str(fields))
135+
)
136+
errors.update(
137+
self._error_as_dict(NON_FIELD_ERRORS, msg)
117138
)
118-
errors.update(self._error_as_dict(NON_FIELD_ERRORS, msg))
119139
else:
120-
msg = f'Please provide a value for: "{fields[-1]}"'
121-
errors.update(self._error_as_dict(fields[-1], msg))
140+
field = fields[0]
141+
msg = (
142+
'Please provide a value for: "{field}"'.format(
143+
field=field
144+
)
145+
)
146+
errors.update(self._error_as_dict(field, msg))
122147
break
123148

124149
if field_value_map and none_provided:
125-
msg = (
126-
f'Please omit changes to the following fields: '
127-
f'{field_to_str(fields)}'
150+
msg = "Please omit changes to the following fields: {fields}".format(
151+
fields=field_to_str(fields)
152+
)
153+
errors.update(
154+
self._error_as_dict(NON_FIELD_ERRORS, msg)
128155
)
129-
errors.update(self._error_as_dict(NON_FIELD_ERRORS, msg))
130156
break
131157

132158
missing_fields = [
133-
field_name for field_name in fields
159+
field_name
160+
for field_name in fields
134161
if field_name not in field_value_map.keys()
135162
]
136163

137-
if not validate_one and not at_least_one and not none_provided:
164+
if (
165+
not validate_one
166+
and not at_least_one
167+
and not none_provided
168+
):
138169
for missing_field in missing_fields:
139-
msg = f'Please provide a value for: "{missing_field}"'
140-
errors.update(self._error_as_dict(missing_field, msg))
170+
msg = 'Please provide a value for: "{missing_field}"'.format(
171+
missing_field=missing_field
172+
)
173+
errors.update(
174+
self._error_as_dict(missing_field, msg)
175+
)
141176

142-
elif validate_one and len(fields) - 1 != len(list(missing_fields)):
143-
msg = (
144-
f'Please provide only one of the following fields: {field_to_str(fields)}'
177+
elif validate_one and len(fields) - 1 != len(
178+
list(missing_fields)
179+
):
180+
msg = "Please provide only one of the following fields: {fields}".format(
181+
fields=field_to_str(fields)
182+
)
183+
errors.update(
184+
self._error_as_dict(NON_FIELD_ERRORS, msg)
145185
)
146-
errors.update(self._error_as_dict(NON_FIELD_ERRORS, msg))
147186

148187
except ValueError:
149188
pass
@@ -154,12 +193,14 @@ def _clean_conditional_fields(
154193
def _clean_required_and_optional_fields(self, exclude=None, context=None):
155194
"""Provide extra validation for fields that are required and single selection fields."""
156195
exclude = exclude or []
157-
if any([
158-
self.REQUIRED_TOGGLE_FIELDS,
159-
self.REQUIRED_MIN_FIELDS,
160-
self.REQUIRED_FIELDS,
161-
self.OPTIONAL_TOGGLE_FIELDS,
162-
]):
196+
if any(
197+
[
198+
self.REQUIRED_TOGGLE_FIELDS,
199+
self.REQUIRED_MIN_FIELDS,
200+
self.REQUIRED_FIELDS,
201+
self.OPTIONAL_TOGGLE_FIELDS,
202+
]
203+
):
163204
error_class = self.ERROR_HANDLERS.get(context, ValueError)
164205
found = []
165206
errors = {}
@@ -170,7 +211,9 @@ def _clean_required_and_optional_fields(self, exclude=None, context=None):
170211
raw_value = getattr(self, f.attname)
171212
if f.name in self.REQUIRED_FIELDS:
172213
if raw_value in f.empty_values and not f.has_default():
173-
msg = f'Please provide a value for: "{f.name}".'
214+
msg = 'Please provide a value for: "{field_name}".'.format(
215+
field_name=f.name
216+
)
174217
errors.update(self._error_as_dict(f.name, msg))
175218

176219
for required_min_field in self.REQUIRED_MIN_FIELDS:
@@ -179,7 +222,10 @@ def _clean_required_and_optional_fields(self, exclude=None, context=None):
179222
if raw_value not in f.empty_values:
180223
found.append({f.name: raw_value})
181224
elif raw_value in f.empty_values and f.has_default():
182-
if isinstance(f, models.CharField) and f.get_default() not in f.empty_values:
225+
if (
226+
isinstance(f, models.CharField)
227+
and f.get_default() not in f.empty_values
228+
):
183229
found.append({f.name: f.get_default()})
184230

185231
for required_toggle_field in self.REQUIRED_TOGGLE_FIELDS:
@@ -188,52 +234,74 @@ def _clean_required_and_optional_fields(self, exclude=None, context=None):
188234
if raw_value not in f.empty_values:
189235
found.append({f.name: raw_value})
190236
elif raw_value in f.empty_values and f.has_default():
191-
if isinstance(f, models.CharField) and f.get_default() not in f.empty_values:
237+
if (
238+
isinstance(f, models.CharField)
239+
and f.get_default() not in f.empty_values
240+
):
192241
found.append({f.name: f.get_default()})
193242

194243
for optional_toggle_field in self.OPTIONAL_TOGGLE_FIELDS:
195-
if f.name in optional_toggle_field and raw_value not in f.empty_values:
244+
if (
245+
f.name in optional_toggle_field
246+
and raw_value not in f.empty_values
247+
):
196248
optional.append({f.name: raw_value})
197249

198250
if self.REQUIRED_MIN_FIELDS:
199251
if not found:
200-
fields_str = '\n, '.join([
201-
field_to_str(fields) for fields in self.REQUIRED_MIN_FIELDS
202-
])
203-
fields_str = f'\n {fields_str}' if len(self.REQUIRED_MIN_FIELDS) > 1 else fields_str
204-
msg = f'Please provide a valid value for any of the following fields: {fields_str}'
252+
fields_str = "\n, ".join(
253+
[field_to_str(fields) for fields in self.REQUIRED_MIN_FIELDS]
254+
)
255+
fields_str = (
256+
"\n {fields}".format(fields=fields_str)
257+
if len(self.REQUIRED_MIN_FIELDS) > 1
258+
else fields_str
259+
)
260+
msg = "Please provide a valid value for any of the following fields: {fields}".format(
261+
fields=fields_str
262+
)
205263
errors.update(self._error_as_dict(NON_FIELD_ERRORS, msg))
206264

207265
if self.REQUIRED_TOGGLE_FIELDS:
208266
if not found:
209-
fields_str = '\n, '.join([
210-
field_to_str(fields) for fields in self.REQUIRED_TOGGLE_FIELDS
211-
])
212-
fields_str = f'\n {fields_str}' if len(self.REQUIRED_TOGGLE_FIELDS) > 1 else fields_str
213-
msg = f'Please provide a valid value for any of the following fields: {fields_str}'
267+
fields_str = "\n, ".join(
268+
[field_to_str(fields) for fields in self.REQUIRED_TOGGLE_FIELDS]
269+
)
270+
fields_str = (
271+
"\n {fields}".format(fields=fields_str)
272+
if len(self.REQUIRED_TOGGLE_FIELDS) > 1
273+
else fields_str
274+
)
275+
msg = "Please provide a valid value for any of the following fields: {fields}".format(
276+
fields=fields_str
277+
)
214278
errors.update(self._error_as_dict(NON_FIELD_ERRORS, msg))
215279
else:
216280
found_keys = [k for item in found for k in item.keys()]
217281
errors.update(
218-
self._validate_only_one_option(self.REQUIRED_TOGGLE_FIELDS, found_keys),
282+
self._validate_only_one_option(
283+
self.REQUIRED_TOGGLE_FIELDS, found_keys
284+
),
219285
)
220286

221287
if self.OPTIONAL_TOGGLE_FIELDS:
222288
if optional:
223289
optional_keys = [k for item in optional for k in item.keys()]
224290
errors.update(
225-
self._validate_only_one_option(self.OPTIONAL_TOGGLE_FIELDS, optional_keys),
291+
self._validate_only_one_option(
292+
self.OPTIONAL_TOGGLE_FIELDS, optional_keys
293+
),
226294
)
227295

228296
if errors:
229297
raise error_class(errors)
230298

231299
def clean_fields(self, exclude=None):
232-
self._clean_conditional_toggle_fields(exclude=exclude, context='form')
233-
self._clean_conditional_min_fields(exclude=exclude, context='form')
234-
self._clean_conditional_empty_fields(exclude=exclude, context='form')
235-
self._clean_conditional_fields(exclude=exclude, context='form')
236-
self._clean_required_and_optional_fields(exclude=exclude, context='form')
300+
self._clean_conditional_toggle_fields(exclude=exclude, context="form")
301+
self._clean_conditional_min_fields(exclude=exclude, context="form")
302+
self._clean_conditional_empty_fields(exclude=exclude, context="form")
303+
self._clean_conditional_fields(exclude=exclude, context="form")
304+
self._clean_required_and_optional_fields(exclude=exclude, context="form")
237305
return super(FieldValidationMixin, self).clean_fields(exclude=exclude)
238306

239307
def save(self, *args, **kwargs):

0 commit comments

Comments
 (0)