123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- # -*- coding: utf-8 -*-
- from __future__ import unicode_literals
- import six
- from library.validator.exceptions import ValidationError, FieldRequiredError, FieldValidationError, BaseValidationError
- from library.validator.fields import BaseField, EMPTY_VALUE, create_field, DictField
- from library.validator.utils import force_str
- class ValidatorMetaClass(type):
- def __new__(cls, cls_name, bases, attrs):
- fields_map = dict()
- parent_fields_map = dict()
- for parent in bases:
- if hasattr(parent, '_FIELDS_MAP'):
- parent_fields_map.update(parent._FIELDS_MAP)
- for name, value in six.iteritems(attrs):
- if isinstance(value, BaseField):
- fields_map[name] = value
- for name in fields_map:
- attrs.pop(name, None)
- parent_fields_map.update(fields_map)
- attrs['_FIELDS_MAP'] = parent_fields_map
- return super(ValidatorMetaClass, cls).__new__(cls, cls_name, bases, attrs)
- @six.add_metaclass(ValidatorMetaClass)
- class Validator(object):
- """ a data validator like Django ORM
- """
- def __init__(self, raw_data):
- """
- :param raw_data: unvalidate data
- """
- assert isinstance(raw_data, dict), '"raw_data" must be a dict, not "{}"'.format(type(raw_data).__name__)
- self.raw_data = raw_data
- self.validated_data = None
- self.errors = {}
- def _validate(self):
- data = {}
- for name, field in six.iteritems(self._FIELDS_MAP):
- value = self.raw_data.get(name, field.get_default())
- if value is EMPTY_VALUE:
- if field.is_required():
- # 对其进行改造 如果这个地方存在自定义的FieldRequiredError的情况下 上报自定义的
- self.errors[field.unicodeName() or name] = field.requiredError()
- continue
- # dont need to validate None
- if value is None:
- data[name] = None
- continue
- try:
- validated_value = field.validate(value)
- internal_value = field.to_internal(validated_value)
- field_validator = getattr(
- self, 'validate_{}'.format(name), None)
- if field_validator and callable(field_validator):
- field_validator(internal_value)
- data[name] = internal_value
- except FieldValidationError as e:
- self.errors[field.unicodeName() or name] = e
- if self.errors:
- return
- try:
- data = self.validate(data)
- except BaseValidationError as e:
- self.errors['error'] = e
- if not self.errors:
- self.validated_data = data
- def is_valid(self, raise_error=False):
- self._validate()
- if raise_error and self.errors:
- raise ValidationError(self.errors)
- return False if self.errors else True
- def validate(self, data):
- """
- model-level validate.
- sub-class can override this method to validate data, return modified data
- """
- return data
- @property
- def str_errors(self):
- errors = dict()
- for name, error in six.iteritems(self.errors):
- errors[name] = error.get_detail()
- return errors
- @classmethod
- def to_dict(cls):
- """
- format Validator to dict
- """
- d = dict()
- for name, field in six.iteritems(cls._FIELDS_MAP):
- field_info = field.to_dict()
- d[name] = field_info
- return d
- @classmethod
- def mock_data(cls):
- """
- return random mocking data.
- mocking data will be valid in most case, but it maybe can't pass from your own `validate` method or `validator`
- """
- mocking_data = {}
- for name, field in six.iteritems(cls._FIELDS_MAP):
- mocking_data[name] = field.mock_data()
- return mocking_data
- def _format(self):
- fields = []
- for name, field in six.iteritems(self._FIELDS_MAP):
- fields.append('{0}:{1}'.format(name, field.FIELD_TYPE_NAME))
- fields = ','.join(fields)
- if len(fields) > 103:
- fields = fields[:100]
- return '<{0}: {1}>'.format(self.__class__.__name__, fields)
- def __str__(self):
- return self._format()
- def __repr__(self):
- return self._format()
- def create_validator(data_struct_dict, name=None):
- """
- create a Validator instance from data_struct_dict
- :param data_struct_dict: a dict describe validator's fields, like the dict `to_dict()` method returned.
- :param name: name of Validator class
- :return: Validator instance
- """
- if name is None:
- name = 'FromDictValidator'
- attrs = {}
- for field_name, field_info in six.iteritems(data_struct_dict):
- field_type = field_info['type']
- if field_type == DictField.FIELD_TYPE_NAME and isinstance(field_info.get('validator'), dict):
- field_info['validator'] = create_validator(field_info['validator'])
- attrs[field_name] = create_field(field_info)
- name = force_str(name)
- return type(name, (Validator, ), attrs)
|