PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/django/core/validators.py

https://github.com/insane/django
Python | 223 lines | 182 code | 32 blank | 9 comment | 23 complexity | e18272bed4cac997f8830dae1e9243f6 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. from __future__ import unicode_literals
  2. import re
  3. try:
  4. from urllib.parse import urlsplit, urlunsplit
  5. except ImportError: # Python 2
  6. from urlparse import urlsplit, urlunsplit
  7. from django.core.exceptions import ValidationError
  8. from django.utils.translation import ugettext_lazy as _, ungettext_lazy
  9. from django.utils.encoding import force_text
  10. from django.utils.ipv6 import is_valid_ipv6_address
  11. from django.utils import six
  12. # These values, if given to validate(), will trigger the self.required check.
  13. EMPTY_VALUES = (None, '', [], (), {})
  14. class RegexValidator(object):
  15. regex = ''
  16. message = _('Enter a valid value.')
  17. code = 'invalid'
  18. def __init__(self, regex=None, message=None, code=None):
  19. if regex is not None:
  20. self.regex = regex
  21. if message is not None:
  22. self.message = message
  23. if code is not None:
  24. self.code = code
  25. # Compile the regex if it was not passed pre-compiled.
  26. if isinstance(self.regex, six.string_types):
  27. self.regex = re.compile(self.regex)
  28. def __call__(self, value):
  29. """
  30. Validates that the input matches the regular expression.
  31. """
  32. if not self.regex.search(force_text(value)):
  33. raise ValidationError(self.message, code=self.code)
  34. class URLValidator(RegexValidator):
  35. regex = re.compile(
  36. r'^(?:http|ftp)s?://' # http:// or https://
  37. r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain...
  38. r'localhost|' # localhost...
  39. r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|' # ...or ipv4
  40. r'\[?[A-F0-9]*:[A-F0-9:]+\]?)' # ...or ipv6
  41. r'(?::\d+)?' # optional port
  42. r'(?:/?|[/?]\S+)$', re.IGNORECASE)
  43. message = _('Enter a valid URL.')
  44. def __call__(self, value):
  45. try:
  46. super(URLValidator, self).__call__(value)
  47. except ValidationError as e:
  48. # Trivial case failed. Try for possible IDN domain
  49. if value:
  50. value = force_text(value)
  51. scheme, netloc, path, query, fragment = urlsplit(value)
  52. try:
  53. netloc = netloc.encode('idna').decode('ascii') # IDN -> ACE
  54. except UnicodeError: # invalid domain part
  55. raise e
  56. url = urlunsplit((scheme, netloc, path, query, fragment))
  57. super(URLValidator, self).__call__(url)
  58. else:
  59. raise
  60. else:
  61. url = value
  62. def validate_integer(value):
  63. try:
  64. int(value)
  65. except (ValueError, TypeError):
  66. raise ValidationError(_('Enter a valid integer.'), code='invalid')
  67. class EmailValidator(object):
  68. message = _('Enter a valid email address.')
  69. code = 'invalid'
  70. user_regex = re.compile(
  71. r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*$" # dot-atom
  72. r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"$)', # quoted-string
  73. re.IGNORECASE)
  74. domain_regex = re.compile(
  75. r'(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}|[A-Z0-9-]{2,})\.?$' # domain
  76. # literal form, ipv4 address (SMTP 4.1.3)
  77. r'|^\[(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\]$',
  78. re.IGNORECASE)
  79. domain_whitelist = ['localhost']
  80. def __init__(self, message=None, code=None, whitelist=None):
  81. if message is not None:
  82. self.message = message
  83. if code is not None:
  84. self.code = code
  85. if whitelist is not None:
  86. self.domain_whitelist = whitelist
  87. def __call__(self, value):
  88. value = force_text(value)
  89. if not value or '@' not in value:
  90. raise ValidationError(self.message, code=self.code)
  91. user_part, domain_part = value.rsplit('@', 1)
  92. if not self.user_regex.match(user_part):
  93. raise ValidationError(self.message, code=self.code)
  94. if (not domain_part in self.domain_whitelist and
  95. not self.domain_regex.match(domain_part)):
  96. # Try for possible IDN domain-part
  97. try:
  98. domain_part = domain_part.encode('idna').decode('ascii')
  99. if not self.domain_regex.match(domain_part):
  100. raise ValidationError(self.message, code=self.code)
  101. else:
  102. return
  103. except UnicodeError:
  104. pass
  105. raise ValidationError(self.message, code=self.code)
  106. validate_email = EmailValidator()
  107. slug_re = re.compile(r'^[-a-zA-Z0-9_]+$')
  108. validate_slug = RegexValidator(slug_re, _("Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."), 'invalid')
  109. ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$')
  110. validate_ipv4_address = RegexValidator(ipv4_re, _('Enter a valid IPv4 address.'), 'invalid')
  111. def validate_ipv6_address(value):
  112. if not is_valid_ipv6_address(value):
  113. raise ValidationError(_('Enter a valid IPv6 address.'), code='invalid')
  114. def validate_ipv46_address(value):
  115. try:
  116. validate_ipv4_address(value)
  117. except ValidationError:
  118. try:
  119. validate_ipv6_address(value)
  120. except ValidationError:
  121. raise ValidationError(_('Enter a valid IPv4 or IPv6 address.'), code='invalid')
  122. ip_address_validator_map = {
  123. 'both': ([validate_ipv46_address], _('Enter a valid IPv4 or IPv6 address.')),
  124. 'ipv4': ([validate_ipv4_address], _('Enter a valid IPv4 address.')),
  125. 'ipv6': ([validate_ipv6_address], _('Enter a valid IPv6 address.')),
  126. }
  127. def ip_address_validators(protocol, unpack_ipv4):
  128. """
  129. Depending on the given parameters returns the appropriate validators for
  130. the GenericIPAddressField.
  131. This code is here, because it is exactly the same for the model and the form field.
  132. """
  133. if protocol != 'both' and unpack_ipv4:
  134. raise ValueError(
  135. "You can only use `unpack_ipv4` if `protocol` is set to 'both'")
  136. try:
  137. return ip_address_validator_map[protocol.lower()]
  138. except KeyError:
  139. raise ValueError("The protocol '%s' is unknown. Supported: %s"
  140. % (protocol, list(ip_address_validator_map)))
  141. comma_separated_int_list_re = re.compile('^[\d,]+$')
  142. validate_comma_separated_integer_list = RegexValidator(comma_separated_int_list_re, _('Enter only digits separated by commas.'), 'invalid')
  143. class BaseValidator(object):
  144. compare = lambda self, a, b: a is not b
  145. clean = lambda self, x: x
  146. message = _('Ensure this value is %(limit_value)s (it is %(show_value)s).')
  147. code = 'limit_value'
  148. def __init__(self, limit_value):
  149. self.limit_value = limit_value
  150. def __call__(self, value):
  151. cleaned = self.clean(value)
  152. params = {'limit_value': self.limit_value, 'show_value': cleaned}
  153. if self.compare(cleaned, self.limit_value):
  154. raise ValidationError(self.message, code=self.code, params=params)
  155. class MaxValueValidator(BaseValidator):
  156. compare = lambda self, a, b: a > b
  157. message = _('Ensure this value is less than or equal to %(limit_value)s.')
  158. code = 'max_value'
  159. class MinValueValidator(BaseValidator):
  160. compare = lambda self, a, b: a < b
  161. message = _('Ensure this value is greater than or equal to %(limit_value)s.')
  162. code = 'min_value'
  163. class MinLengthValidator(BaseValidator):
  164. compare = lambda self, a, b: a < b
  165. clean = lambda self, x: len(x)
  166. message = ungettext_lazy(
  167. 'Ensure this value has at least %(limit_value)d character (it has %(show_value)d).',
  168. 'Ensure this value has at least %(limit_value)d characters (it has %(show_value)d).',
  169. 'limit_value')
  170. code = 'min_length'
  171. class MaxLengthValidator(BaseValidator):
  172. compare = lambda self, a, b: a > b
  173. clean = lambda self, x: len(x)
  174. message = ungettext_lazy(
  175. 'Ensure this value has at most %(limit_value)d character (it has %(show_value)d).',
  176. 'Ensure this value has at most %(limit_value)d characters (it has %(show_value)d).',
  177. 'limit_value')
  178. code = 'max_length'