validator.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. import six
  4. from library.validator.exceptions import ValidationError, FieldRequiredError, FieldValidationError, BaseValidationError
  5. from library.validator.fields import BaseField, EMPTY_VALUE, create_field, DictField
  6. from library.validator.utils import force_str
  7. class ValidatorMetaClass(type):
  8. def __new__(cls, cls_name, bases, attrs):
  9. fields_map = dict()
  10. parent_fields_map = dict()
  11. for parent in bases:
  12. if hasattr(parent, '_FIELDS_MAP'):
  13. parent_fields_map.update(parent._FIELDS_MAP)
  14. for name, value in six.iteritems(attrs):
  15. if isinstance(value, BaseField):
  16. fields_map[name] = value
  17. for name in fields_map:
  18. attrs.pop(name, None)
  19. parent_fields_map.update(fields_map)
  20. attrs['_FIELDS_MAP'] = parent_fields_map
  21. return super(ValidatorMetaClass, cls).__new__(cls, cls_name, bases, attrs)
  22. @six.add_metaclass(ValidatorMetaClass)
  23. class Validator(object):
  24. """ a data validator like Django ORM
  25. """
  26. def __init__(self, raw_data):
  27. """
  28. :param raw_data: unvalidate data
  29. """
  30. assert isinstance(raw_data, dict), '"raw_data" must be a dict, not "{}"'.format(type(raw_data).__name__)
  31. self.raw_data = raw_data
  32. self.validated_data = None
  33. self.errors = {}
  34. def _validate(self):
  35. data = {}
  36. for name, field in six.iteritems(self._FIELDS_MAP):
  37. value = self.raw_data.get(name, field.get_default())
  38. if value is EMPTY_VALUE:
  39. if field.is_required():
  40. # 对其进行改造 如果这个地方存在自定义的FieldRequiredError的情况下 上报自定义的
  41. self.errors[field.unicodeName() or name] = field.requiredError()
  42. continue
  43. # dont need to validate None
  44. if value is None:
  45. data[name] = None
  46. continue
  47. try:
  48. validated_value = field.validate(value)
  49. internal_value = field.to_internal(validated_value)
  50. field_validator = getattr(
  51. self, 'validate_{}'.format(name), None)
  52. if field_validator and callable(field_validator):
  53. field_validator(internal_value)
  54. data[name] = internal_value
  55. except FieldValidationError as e:
  56. self.errors[field.unicodeName() or name] = e
  57. if self.errors:
  58. return
  59. try:
  60. data = self.validate(data)
  61. except BaseValidationError as e:
  62. self.errors['error'] = e
  63. if not self.errors:
  64. self.validated_data = data
  65. def is_valid(self, raise_error=False):
  66. self._validate()
  67. if raise_error and self.errors:
  68. raise ValidationError(self.errors)
  69. return False if self.errors else True
  70. def validate(self, data):
  71. """
  72. model-level validate.
  73. sub-class can override this method to validate data, return modified data
  74. """
  75. return data
  76. @property
  77. def str_errors(self):
  78. errors = dict()
  79. for name, error in six.iteritems(self.errors):
  80. errors[name] = error.get_detail()
  81. return errors
  82. @classmethod
  83. def to_dict(cls):
  84. """
  85. format Validator to dict
  86. """
  87. d = dict()
  88. for name, field in six.iteritems(cls._FIELDS_MAP):
  89. field_info = field.to_dict()
  90. d[name] = field_info
  91. return d
  92. @classmethod
  93. def mock_data(cls):
  94. """
  95. return random mocking data.
  96. mocking data will be valid in most case, but it maybe can't pass from your own `validate` method or `validator`
  97. """
  98. mocking_data = {}
  99. for name, field in six.iteritems(cls._FIELDS_MAP):
  100. mocking_data[name] = field.mock_data()
  101. return mocking_data
  102. def _format(self):
  103. fields = []
  104. for name, field in six.iteritems(self._FIELDS_MAP):
  105. fields.append('{0}:{1}'.format(name, field.FIELD_TYPE_NAME))
  106. fields = ','.join(fields)
  107. if len(fields) > 103:
  108. fields = fields[:100]
  109. return '<{0}: {1}>'.format(self.__class__.__name__, fields)
  110. def __str__(self):
  111. return self._format()
  112. def __repr__(self):
  113. return self._format()
  114. def create_validator(data_struct_dict, name=None):
  115. """
  116. create a Validator instance from data_struct_dict
  117. :param data_struct_dict: a dict describe validator's fields, like the dict `to_dict()` method returned.
  118. :param name: name of Validator class
  119. :return: Validator instance
  120. """
  121. if name is None:
  122. name = 'FromDictValidator'
  123. attrs = {}
  124. for field_name, field_info in six.iteritems(data_struct_dict):
  125. field_type = field_info['type']
  126. if field_type == DictField.FIELD_TYPE_NAME and isinstance(field_info.get('validator'), dict):
  127. field_info['validator'] = create_validator(field_info['validator'])
  128. attrs[field_name] = create_field(field_info)
  129. name = force_str(name)
  130. return type(name, (Validator, ), attrs)