mirror of
https://github.com/django/django.git
synced 2025-10-24 14:16:09 +00:00
Removed oldforms, validators, and related code:
* Removed `Manipulator`, `AutomaticManipulator`, and related classes.
* Removed oldforms specific bits from model fields:
* Removed `validator_list` and `core` arguments from constructors.
* Removed the methods:
* `get_manipulator_field_names`
* `get_manipulator_field_objs`
* `get_manipulator_fields`
* `get_manipulator_new_data`
* `prepare_field_objs_and_params`
* `get_follow`
* Renamed `flatten_data` method to `value_to_string` for better alignment with its use by the serialization framework, which was the only remaining code using `flatten_data`.
* Removed oldforms methods from `django.db.models.Options` class: `get_followed_related_objects`, `get_data_holders`, `get_follow`, and `has_field_type`.
* Removed oldforms-admin specific options from `django.db.models.fields.related` classes: `num_in_admin`, `min_num_in_admin`, `max_num_in_admin`, `num_extra_on_change`, and `edit_inline`.
* Serialization framework
* `Serializer.get_string_value` now calls the model fields' renamed `value_to_string` methods.
* Removed a special-casing of `models.DateTimeField` in `core.serializers.base.Serializer.get_string_value` that's handled by `django.db.models.fields.DateTimeField.value_to_string`.
* Removed `django.core.validators`:
* Moved `ValidationError` exception to `django.core.exceptions`.
* For the couple places that were using validators, brought over the necessary code to maintain the same functionality.
* Introduced a SlugField form field for validation and to compliment the SlugField model field (refs #8040).
* Removed an oldforms-style model creation hack (refs #2160).
git-svn-id: http://code.djangoproject.com/svn/django/trunk@8616 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
@@ -32,3 +32,6 @@ class FieldError(Exception):
|
||||
"""Some kind of problem with a model field."""
|
||||
pass
|
||||
|
||||
class ValidationError(Exception):
|
||||
"""An error while validating data."""
|
||||
pass
|
||||
|
||||
@@ -57,12 +57,7 @@ class Serializer(object):
|
||||
"""
|
||||
Convert a field's value to a string.
|
||||
"""
|
||||
if isinstance(field, models.DateTimeField):
|
||||
d = datetime_safe.new_datetime(getattr(obj, field.name))
|
||||
value = d.strftime("%Y-%m-%d %H:%M:%S")
|
||||
else:
|
||||
value = field.flatten_data(follow=None, obj=obj).get(field.name, "")
|
||||
return smart_unicode(value)
|
||||
return smart_unicode(field.value_to_string(obj))
|
||||
|
||||
def start_serialization(self):
|
||||
"""
|
||||
|
||||
@@ -1,598 +0,0 @@
|
||||
"""
|
||||
A library of validators that return None and raise ValidationError when the
|
||||
provided data isn't valid.
|
||||
|
||||
Validators may be callable classes, and they may have an 'always_test'
|
||||
attribute. If an 'always_test' attribute exists (regardless of value), the
|
||||
validator will *always* be run, regardless of whether its associated
|
||||
form field is required.
|
||||
"""
|
||||
|
||||
import urllib2
|
||||
import re
|
||||
try:
|
||||
from decimal import Decimal, DecimalException
|
||||
except ImportError:
|
||||
from django.utils._decimal import Decimal, DecimalException # Python 2.3
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy, ungettext
|
||||
from django.utils.functional import Promise, lazy
|
||||
from django.utils.encoding import force_unicode, smart_str
|
||||
|
||||
_datere = r'\d{4}-\d{1,2}-\d{1,2}'
|
||||
_timere = r'(?:[01]?[0-9]|2[0-3]):[0-5][0-9](?::[0-5][0-9])?'
|
||||
alnum_re = re.compile(r'^\w+$')
|
||||
alnumurl_re = re.compile(r'^[-\w/]+$')
|
||||
ansi_date_re = re.compile('^%s$' % _datere)
|
||||
ansi_time_re = re.compile('^%s$' % _timere)
|
||||
ansi_datetime_re = re.compile('^%s %s$' % (_datere, _timere))
|
||||
email_re = re.compile(
|
||||
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom
|
||||
r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"' # quoted-string
|
||||
r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain
|
||||
integer_re = re.compile(r'^-?\d+$')
|
||||
ip4_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}$')
|
||||
phone_re = re.compile(r'^[A-PR-Y0-9]{3}-[A-PR-Y0-9]{3}-[A-PR-Y0-9]{4}$', re.IGNORECASE)
|
||||
slug_re = re.compile(r'^[-\w]+$')
|
||||
url_re = re.compile(r'^https?://\S+$')
|
||||
|
||||
lazy_inter = lazy(lambda a,b: force_unicode(a) % b, unicode)
|
||||
|
||||
class ValidationError(Exception):
|
||||
def __init__(self, message):
|
||||
"ValidationError can be passed a string or a list."
|
||||
if isinstance(message, list):
|
||||
self.messages = [force_unicode(msg) for msg in message]
|
||||
else:
|
||||
assert isinstance(message, (basestring, Promise)), ("%s should be a string" % repr(message))
|
||||
self.messages = [force_unicode(message)]
|
||||
|
||||
def __str__(self):
|
||||
# This is needed because, without a __str__(), printing an exception
|
||||
# instance would result in this:
|
||||
# AttributeError: ValidationError instance has no attribute 'args'
|
||||
# See http://www.python.org/doc/current/tut/node10.html#handling
|
||||
return str(self.messages)
|
||||
|
||||
class CriticalValidationError(Exception):
|
||||
def __init__(self, message):
|
||||
"ValidationError can be passed a string or a list."
|
||||
if isinstance(message, list):
|
||||
self.messages = [force_unicode(msg) for msg in message]
|
||||
else:
|
||||
assert isinstance(message, (basestring, Promise)), ("'%s' should be a string" % message)
|
||||
self.messages = [force_unicode(message)]
|
||||
|
||||
def __str__(self):
|
||||
return str(self.messages)
|
||||
|
||||
def isAlphaNumeric(field_data, all_data):
|
||||
if not alnum_re.search(field_data):
|
||||
raise ValidationError, _("This value must contain only letters, numbers and underscores.")
|
||||
|
||||
def isAlphaNumericURL(field_data, all_data):
|
||||
if not alnumurl_re.search(field_data):
|
||||
raise ValidationError, _("This value must contain only letters, numbers, underscores, dashes or slashes.")
|
||||
|
||||
def isSlug(field_data, all_data):
|
||||
if not slug_re.search(field_data):
|
||||
raise ValidationError, _("This value must contain only letters, numbers, underscores or hyphens.")
|
||||
|
||||
def isLowerCase(field_data, all_data):
|
||||
if field_data.lower() != field_data:
|
||||
raise ValidationError, _("Uppercase letters are not allowed here.")
|
||||
|
||||
def isUpperCase(field_data, all_data):
|
||||
if field_data.upper() != field_data:
|
||||
raise ValidationError, _("Lowercase letters are not allowed here.")
|
||||
|
||||
def isCommaSeparatedIntegerList(field_data, all_data):
|
||||
for supposed_int in field_data.split(','):
|
||||
try:
|
||||
int(supposed_int)
|
||||
except ValueError:
|
||||
raise ValidationError, _("Enter only digits separated by commas.")
|
||||
|
||||
def isCommaSeparatedEmailList(field_data, all_data):
|
||||
"""
|
||||
Checks that field_data is a string of e-mail addresses separated by commas.
|
||||
Blank field_data values will not throw a validation error, and whitespace
|
||||
is allowed around the commas.
|
||||
"""
|
||||
for supposed_email in field_data.split(','):
|
||||
try:
|
||||
isValidEmail(supposed_email.strip(), '')
|
||||
except ValidationError:
|
||||
raise ValidationError, _("Enter valid e-mail addresses separated by commas.")
|
||||
|
||||
def isValidIPAddress4(field_data, all_data):
|
||||
if not ip4_re.search(field_data):
|
||||
raise ValidationError, _("Please enter a valid IP address.")
|
||||
|
||||
def isNotEmpty(field_data, all_data):
|
||||
if field_data.strip() == '':
|
||||
raise ValidationError, _("Empty values are not allowed here.")
|
||||
|
||||
def isOnlyDigits(field_data, all_data):
|
||||
if not field_data.isdigit():
|
||||
raise ValidationError, _("Non-numeric characters aren't allowed here.")
|
||||
|
||||
def isNotOnlyDigits(field_data, all_data):
|
||||
if field_data.isdigit():
|
||||
raise ValidationError, _("This value can't be comprised solely of digits.")
|
||||
|
||||
def isInteger(field_data, all_data):
|
||||
# This differs from isOnlyDigits because this accepts the negative sign
|
||||
if not integer_re.search(field_data):
|
||||
raise ValidationError, _("Enter a whole number.")
|
||||
|
||||
def isOnlyLetters(field_data, all_data):
|
||||
if not field_data.isalpha():
|
||||
raise ValidationError, _("Only alphabetical characters are allowed here.")
|
||||
|
||||
def _isValidDate(date_string):
|
||||
"""
|
||||
A helper function used by isValidANSIDate and isValidANSIDatetime to
|
||||
check if the date is valid. The date string is assumed to already be in
|
||||
YYYY-MM-DD format.
|
||||
"""
|
||||
from datetime import date
|
||||
# Could use time.strptime here and catch errors, but datetime.date below
|
||||
# produces much friendlier error messages.
|
||||
year, month, day = map(int, date_string.split('-'))
|
||||
try:
|
||||
date(year, month, day)
|
||||
except ValueError, e:
|
||||
msg = _('Invalid date: %s') % _(str(e))
|
||||
raise ValidationError, msg
|
||||
|
||||
def isValidANSIDate(field_data, all_data):
|
||||
if not ansi_date_re.search(field_data):
|
||||
raise ValidationError, _('Enter a valid date in YYYY-MM-DD format.')
|
||||
_isValidDate(field_data)
|
||||
|
||||
def isValidANSITime(field_data, all_data):
|
||||
if not ansi_time_re.search(field_data):
|
||||
raise ValidationError, _('Enter a valid time in HH:MM format.')
|
||||
|
||||
def isValidANSIDatetime(field_data, all_data):
|
||||
if not ansi_datetime_re.search(field_data):
|
||||
raise ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM format.')
|
||||
_isValidDate(field_data.split()[0])
|
||||
|
||||
def isValidEmail(field_data, all_data):
|
||||
if not email_re.search(field_data):
|
||||
raise ValidationError, _('Enter a valid e-mail address.')
|
||||
|
||||
def isValidImage(field_data, all_data):
|
||||
"""
|
||||
Checks that the file-upload field data contains a valid image (GIF, JPG,
|
||||
PNG, possibly others -- whatever the Python Imaging Library supports).
|
||||
"""
|
||||
from PIL import Image
|
||||
from cStringIO import StringIO
|
||||
try:
|
||||
content = field_data.read()
|
||||
except TypeError:
|
||||
raise ValidationError, _("No file was submitted. Check the encoding type on the form.")
|
||||
try:
|
||||
# load() is the only method that can spot a truncated JPEG,
|
||||
# but it cannot be called sanely after verify()
|
||||
trial_image = Image.open(StringIO(content))
|
||||
trial_image.load()
|
||||
# verify() is the only method that can spot a corrupt PNG,
|
||||
# but it must be called immediately after the constructor
|
||||
trial_image = Image.open(StringIO(content))
|
||||
trial_image.verify()
|
||||
except Exception: # Python Imaging Library doesn't recognize it as an image
|
||||
raise ValidationError, _("Upload a valid image. The file you uploaded was either not an image or a corrupted image.")
|
||||
|
||||
def isValidImageURL(field_data, all_data):
|
||||
uc = URLMimeTypeCheck(('image/jpeg', 'image/gif', 'image/png'))
|
||||
try:
|
||||
uc(field_data, all_data)
|
||||
except URLMimeTypeCheck.InvalidContentType:
|
||||
raise ValidationError, _("The URL %s does not point to a valid image.") % field_data
|
||||
|
||||
def isValidPhone(field_data, all_data):
|
||||
if not phone_re.search(field_data):
|
||||
raise ValidationError, _('Phone numbers must be in XXX-XXX-XXXX format. "%s" is invalid.') % field_data
|
||||
|
||||
def isValidQuicktimeVideoURL(field_data, all_data):
|
||||
"Checks that the given URL is a video that can be played by QuickTime (qt, mpeg)"
|
||||
uc = URLMimeTypeCheck(('video/quicktime', 'video/mpeg',))
|
||||
try:
|
||||
uc(field_data, all_data)
|
||||
except URLMimeTypeCheck.InvalidContentType:
|
||||
raise ValidationError, _("The URL %s does not point to a valid QuickTime video.") % field_data
|
||||
|
||||
def isValidURL(field_data, all_data):
|
||||
if not url_re.search(field_data):
|
||||
raise ValidationError, _("A valid URL is required.")
|
||||
|
||||
def isValidHTML(field_data, all_data):
|
||||
import urllib, urllib2
|
||||
try:
|
||||
u = urllib2.urlopen('http://validator.w3.org/check', urllib.urlencode({'fragment': field_data, 'output': 'xml'}))
|
||||
except:
|
||||
# Validator or Internet connection is unavailable. Fail silently.
|
||||
return
|
||||
html_is_valid = (u.headers.get('x-w3c-validator-status', 'Invalid') == 'Valid')
|
||||
if html_is_valid:
|
||||
return
|
||||
from xml.dom.minidom import parseString
|
||||
error_messages = [e.firstChild.wholeText for e in parseString(u.read()).getElementsByTagName('messages')[0].getElementsByTagName('msg')]
|
||||
raise ValidationError, _("Valid HTML is required. Specific errors are:\n%s") % "\n".join(error_messages)
|
||||
|
||||
def isWellFormedXml(field_data, all_data):
|
||||
from xml.dom.minidom import parseString
|
||||
try:
|
||||
parseString(field_data)
|
||||
except Exception, e: # Naked except because we're not sure what will be thrown
|
||||
raise ValidationError, _("Badly formed XML: %s") % str(e)
|
||||
|
||||
def isWellFormedXmlFragment(field_data, all_data):
|
||||
isWellFormedXml('<root>%s</root>' % field_data, all_data)
|
||||
|
||||
def isExistingURL(field_data, all_data):
|
||||
try:
|
||||
headers = {
|
||||
"Accept" : "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
|
||||
"Accept-Language" : "en-us,en;q=0.5",
|
||||
"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
|
||||
"Connection" : "close",
|
||||
"User-Agent": settings.URL_VALIDATOR_USER_AGENT
|
||||
}
|
||||
req = urllib2.Request(field_data,None, headers)
|
||||
u = urllib2.urlopen(req)
|
||||
except ValueError:
|
||||
raise ValidationError, _("Invalid URL: %s") % field_data
|
||||
except urllib2.HTTPError, e:
|
||||
# 401s are valid; they just mean authorization is required.
|
||||
# 301 and 302 are redirects; they just mean look somewhere else.
|
||||
if str(e.code) not in ('401','301','302'):
|
||||
raise ValidationError, _("The URL %s is a broken link.") % field_data
|
||||
except: # urllib2.URLError, httplib.InvalidURL, etc.
|
||||
raise ValidationError, _("The URL %s is a broken link.") % field_data
|
||||
|
||||
def isValidUSState(field_data, all_data):
|
||||
"Checks that the given string is a valid two-letter U.S. state abbreviation"
|
||||
states = ['AA', 'AE', 'AK', 'AL', 'AP', 'AR', 'AS', 'AZ', 'CA', 'CO', 'CT', 'DC', 'DE', 'FL', 'FM', 'GA', 'GU', 'HI', 'IA', 'ID', 'IL', 'IN', 'KS', 'KY', 'LA', 'MA', 'MD', 'ME', 'MH', 'MI', 'MN', 'MO', 'MP', 'MS', 'MT', 'NC', 'ND', 'NE', 'NH', 'NJ', 'NM', 'NV', 'NY', 'OH', 'OK', 'OR', 'PA', 'PR', 'PW', 'RI', 'SC', 'SD', 'TN', 'TX', 'UT', 'VA', 'VI', 'VT', 'WA', 'WI', 'WV', 'WY']
|
||||
if field_data.upper() not in states:
|
||||
raise ValidationError, _("Enter a valid U.S. state abbreviation.")
|
||||
|
||||
def hasNoProfanities(field_data, all_data):
|
||||
"""
|
||||
Checks that the given string has no profanities in it. This does a simple
|
||||
check for whether each profanity exists within the string, so 'fuck' will
|
||||
catch 'motherfucker' as well. Raises a ValidationError such as:
|
||||
Watch your mouth! The words "f--k" and "s--t" are not allowed here.
|
||||
"""
|
||||
field_data = field_data.lower() # normalize
|
||||
words_seen = [w for w in settings.PROFANITIES_LIST if w in field_data]
|
||||
if words_seen:
|
||||
from django.utils.text import get_text_list
|
||||
plural = len(words_seen)
|
||||
raise ValidationError, ungettext("Watch your mouth! The word %s is not allowed here.",
|
||||
"Watch your mouth! The words %s are not allowed here.", plural) % \
|
||||
get_text_list(['"%s%s%s"' % (i[0], '-'*(len(i)-2), i[-1]) for i in words_seen], _('and'))
|
||||
|
||||
class AlwaysMatchesOtherField(object):
|
||||
def __init__(self, other_field_name, error_message=None):
|
||||
self.other = other_field_name
|
||||
self.error_message = error_message or lazy_inter(ugettext_lazy("This field must match the '%s' field."), self.other)
|
||||
self.always_test = True
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
if field_data != all_data[self.other]:
|
||||
raise ValidationError, self.error_message
|
||||
|
||||
class ValidateIfOtherFieldEquals(object):
|
||||
def __init__(self, other_field, other_value, validator_list):
|
||||
self.other_field, self.other_value = other_field, other_value
|
||||
self.validator_list = validator_list
|
||||
self.always_test = True
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
if self.other_field in all_data and all_data[self.other_field] == self.other_value:
|
||||
for v in self.validator_list:
|
||||
v(field_data, all_data)
|
||||
|
||||
class RequiredIfOtherFieldNotGiven(object):
|
||||
def __init__(self, other_field_name, error_message=ugettext_lazy("Please enter something for at least one field.")):
|
||||
self.other, self.error_message = other_field_name, error_message
|
||||
self.always_test = True
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
if not all_data.get(self.other, False) and not field_data:
|
||||
raise ValidationError, self.error_message
|
||||
|
||||
class RequiredIfOtherFieldsGiven(object):
|
||||
def __init__(self, other_field_names, error_message=ugettext_lazy("Please enter both fields or leave them both empty.")):
|
||||
self.other, self.error_message = other_field_names, error_message
|
||||
self.always_test = True
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
for field in self.other:
|
||||
if all_data.get(field, False) and not field_data:
|
||||
raise ValidationError, self.error_message
|
||||
|
||||
class RequiredIfOtherFieldGiven(RequiredIfOtherFieldsGiven):
|
||||
"Like RequiredIfOtherFieldsGiven, but takes a single field name instead of a list."
|
||||
def __init__(self, other_field_name, error_message=ugettext_lazy("Please enter both fields or leave them both empty.")):
|
||||
RequiredIfOtherFieldsGiven.__init__(self, [other_field_name], error_message)
|
||||
|
||||
class RequiredIfOtherFieldEquals(object):
|
||||
def __init__(self, other_field, other_value, error_message=None, other_label=None):
|
||||
self.other_field = other_field
|
||||
self.other_value = other_value
|
||||
other_label = other_label or other_value
|
||||
self.error_message = error_message or lazy_inter(ugettext_lazy("This field must be given if %(field)s is %(value)s"), {
|
||||
'field': other_field, 'value': other_label})
|
||||
self.always_test = True
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
if self.other_field in all_data and all_data[self.other_field] == self.other_value and not field_data:
|
||||
raise ValidationError(self.error_message)
|
||||
|
||||
class RequiredIfOtherFieldDoesNotEqual(object):
|
||||
def __init__(self, other_field, other_value, other_label=None, error_message=None):
|
||||
self.other_field = other_field
|
||||
self.other_value = other_value
|
||||
other_label = other_label or other_value
|
||||
self.error_message = error_message or lazy_inter(ugettext_lazy("This field must be given if %(field)s is not %(value)s"), {
|
||||
'field': other_field, 'value': other_label})
|
||||
self.always_test = True
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
if self.other_field in all_data and all_data[self.other_field] != self.other_value and not field_data:
|
||||
raise ValidationError(self.error_message)
|
||||
|
||||
class IsLessThanOtherField(object):
|
||||
def __init__(self, other_field_name, error_message):
|
||||
self.other, self.error_message = other_field_name, error_message
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
if field_data > all_data[self.other]:
|
||||
raise ValidationError, self.error_message
|
||||
|
||||
class UniqueAmongstFieldsWithPrefix(object):
|
||||
def __init__(self, field_name, prefix, error_message):
|
||||
self.field_name, self.prefix = field_name, prefix
|
||||
self.error_message = error_message or ugettext_lazy("Duplicate values are not allowed.")
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
for field_name, value in all_data.items():
|
||||
if field_name != self.field_name and value == field_data:
|
||||
raise ValidationError, self.error_message
|
||||
|
||||
class NumberIsInRange(object):
|
||||
"""
|
||||
Validator that tests if a value is in a range (inclusive).
|
||||
"""
|
||||
def __init__(self, lower=None, upper=None, error_message=''):
|
||||
self.lower, self.upper = lower, upper
|
||||
if not error_message:
|
||||
if lower and upper:
|
||||
self.error_message = _("This value must be between %(lower)s and %(upper)s.") % {'lower': lower, 'upper': upper}
|
||||
elif lower:
|
||||
self.error_message = _("This value must be at least %s.") % lower
|
||||
elif upper:
|
||||
self.error_message = _("This value must be no more than %s.") % upper
|
||||
else:
|
||||
self.error_message = error_message
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
# Try to make the value numeric. If this fails, we assume another
|
||||
# validator will catch the problem.
|
||||
try:
|
||||
val = float(field_data)
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
# Now validate
|
||||
if self.lower and self.upper and (val < self.lower or val > self.upper):
|
||||
raise ValidationError(self.error_message)
|
||||
elif self.lower and val < self.lower:
|
||||
raise ValidationError(self.error_message)
|
||||
elif self.upper and val > self.upper:
|
||||
raise ValidationError(self.error_message)
|
||||
|
||||
class IsAPowerOf(object):
|
||||
"""
|
||||
Usage: If you create an instance of the IsPowerOf validator:
|
||||
v = IsAPowerOf(2)
|
||||
|
||||
The following calls will succeed:
|
||||
v(4, None)
|
||||
v(8, None)
|
||||
v(16, None)
|
||||
|
||||
But this call:
|
||||
v(17, None)
|
||||
will raise "django.core.validators.ValidationError: ['This value must be a power of 2.']"
|
||||
"""
|
||||
def __init__(self, power_of):
|
||||
self.power_of = power_of
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
from math import log
|
||||
val = log(int(field_data)) / log(self.power_of)
|
||||
if val != int(val):
|
||||
raise ValidationError, _("This value must be a power of %s.") % self.power_of
|
||||
|
||||
class IsValidDecimal(object):
|
||||
def __init__(self, max_digits, decimal_places):
|
||||
self.max_digits, self.decimal_places = max_digits, decimal_places
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
try:
|
||||
val = Decimal(field_data)
|
||||
except DecimalException:
|
||||
raise ValidationError, _("Please enter a valid decimal number.")
|
||||
|
||||
pieces = str(val).lstrip("-").split('.')
|
||||
decimals = (len(pieces) == 2) and len(pieces[1]) or 0
|
||||
digits = len(pieces[0])
|
||||
|
||||
if digits + decimals > self.max_digits:
|
||||
raise ValidationError, ungettext("Please enter a valid decimal number with at most %s total digit.",
|
||||
"Please enter a valid decimal number with at most %s total digits.", self.max_digits) % self.max_digits
|
||||
if digits > (self.max_digits - self.decimal_places):
|
||||
raise ValidationError, ungettext( "Please enter a valid decimal number with a whole part of at most %s digit.",
|
||||
"Please enter a valid decimal number with a whole part of at most %s digits.", str(self.max_digits-self.decimal_places)) % str(self.max_digits-self.decimal_places)
|
||||
if decimals > self.decimal_places:
|
||||
raise ValidationError, ungettext("Please enter a valid decimal number with at most %s decimal place.",
|
||||
"Please enter a valid decimal number with at most %s decimal places.", self.decimal_places) % self.decimal_places
|
||||
|
||||
def isValidFloat(field_data, all_data):
|
||||
data = smart_str(field_data)
|
||||
try:
|
||||
float(data)
|
||||
except ValueError:
|
||||
raise ValidationError, _("Please enter a valid floating point number.")
|
||||
|
||||
class HasAllowableSize(object):
|
||||
"""
|
||||
Checks that the file-upload field data is a certain size. min_size and
|
||||
max_size are measurements in bytes.
|
||||
"""
|
||||
def __init__(self, min_size=None, max_size=None, min_error_message=None, max_error_message=None):
|
||||
self.min_size, self.max_size = min_size, max_size
|
||||
self.min_error_message = min_error_message or lazy_inter(ugettext_lazy("Make sure your uploaded file is at least %s bytes big."), min_size)
|
||||
self.max_error_message = max_error_message or lazy_inter(ugettext_lazy("Make sure your uploaded file is at most %s bytes big."), max_size)
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
try:
|
||||
content = field_data.read()
|
||||
except TypeError:
|
||||
raise ValidationError, ugettext_lazy("No file was submitted. Check the encoding type on the form.")
|
||||
if self.min_size is not None and len(content) < self.min_size:
|
||||
raise ValidationError, self.min_error_message
|
||||
if self.max_size is not None and len(content) > self.max_size:
|
||||
raise ValidationError, self.max_error_message
|
||||
|
||||
class MatchesRegularExpression(object):
|
||||
"""
|
||||
Checks that the field matches the given regular-expression. The regex
|
||||
should be in string format, not already compiled.
|
||||
"""
|
||||
def __init__(self, regexp, error_message=ugettext_lazy("The format for this field is wrong.")):
|
||||
self.regexp = re.compile(regexp)
|
||||
self.error_message = error_message
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
if not self.regexp.search(field_data):
|
||||
raise ValidationError(self.error_message)
|
||||
|
||||
class AnyValidator(object):
|
||||
"""
|
||||
This validator tries all given validators. If any one of them succeeds,
|
||||
validation passes. If none of them succeeds, the given message is thrown
|
||||
as a validation error. The message is rather unspecific, so it's best to
|
||||
specify one on instantiation.
|
||||
"""
|
||||
def __init__(self, validator_list=None, error_message=ugettext_lazy("This field is invalid.")):
|
||||
if validator_list is None: validator_list = []
|
||||
self.validator_list = validator_list
|
||||
self.error_message = error_message
|
||||
for v in validator_list:
|
||||
if hasattr(v, 'always_test'):
|
||||
self.always_test = True
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
for v in self.validator_list:
|
||||
try:
|
||||
v(field_data, all_data)
|
||||
return
|
||||
except ValidationError, e:
|
||||
pass
|
||||
raise ValidationError(self.error_message)
|
||||
|
||||
class URLMimeTypeCheck(object):
|
||||
"Checks that the provided URL points to a document with a listed mime type"
|
||||
class CouldNotRetrieve(ValidationError):
|
||||
pass
|
||||
class InvalidContentType(ValidationError):
|
||||
pass
|
||||
|
||||
def __init__(self, mime_type_list):
|
||||
self.mime_type_list = mime_type_list
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
import urllib2
|
||||
try:
|
||||
isValidURL(field_data, all_data)
|
||||
except ValidationError:
|
||||
raise
|
||||
try:
|
||||
info = urllib2.urlopen(field_data).info()
|
||||
except (urllib2.HTTPError, urllib2.URLError):
|
||||
raise URLMimeTypeCheck.CouldNotRetrieve, _("Could not retrieve anything from %s.") % field_data
|
||||
content_type = info['content-type']
|
||||
if content_type not in self.mime_type_list:
|
||||
raise URLMimeTypeCheck.InvalidContentType, _("The URL %(url)s returned the invalid Content-Type header '%(contenttype)s'.") % {
|
||||
'url': field_data, 'contenttype': content_type}
|
||||
|
||||
class RelaxNGCompact(object):
|
||||
"Validate against a Relax NG compact schema"
|
||||
def __init__(self, schema_path, additional_root_element=None):
|
||||
self.schema_path = schema_path
|
||||
self.additional_root_element = additional_root_element
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
import os, tempfile
|
||||
if self.additional_root_element:
|
||||
field_data = '<%(are)s>%(data)s\n</%(are)s>' % {
|
||||
'are': self.additional_root_element,
|
||||
'data': field_data
|
||||
}
|
||||
filename = tempfile.mktemp() # Insecure, but nothing else worked
|
||||
fp = open(filename, 'w')
|
||||
fp.write(field_data)
|
||||
fp.close()
|
||||
if not os.path.exists(settings.JING_PATH):
|
||||
raise Exception, "%s not found!" % settings.JING_PATH
|
||||
p = os.popen('%s -c %s %s' % (settings.JING_PATH, self.schema_path, filename))
|
||||
errors = [line.strip() for line in p.readlines()]
|
||||
p.close()
|
||||
os.unlink(filename)
|
||||
display_errors = []
|
||||
lines = field_data.split('\n')
|
||||
for error in errors:
|
||||
ignored, line, level, message = error.split(':', 3)
|
||||
# Scrape the Jing error messages to reword them more nicely.
|
||||
m = re.search(r'Expected "(.*?)" to terminate element starting on line (\d+)', message)
|
||||
if m:
|
||||
display_errors.append(_('Please close the unclosed %(tag)s tag from line %(line)s. (Line starts with "%(start)s".)') % \
|
||||
{'tag':m.group(1).replace('/', ''), 'line':m.group(2), 'start':lines[int(m.group(2)) - 1][:30]})
|
||||
continue
|
||||
if message.strip() == 'text not allowed here':
|
||||
display_errors.append(_('Some text starting on line %(line)s is not allowed in that context. (Line starts with "%(start)s".)') % \
|
||||
{'line':line, 'start':lines[int(line) - 1][:30]})
|
||||
continue
|
||||
m = re.search(r'\s*attribute "(.*?)" not allowed at this point; ignored', message)
|
||||
if m:
|
||||
display_errors.append(_('"%(attr)s" on line %(line)s is an invalid attribute. (Line starts with "%(start)s".)') % \
|
||||
{'attr':m.group(1), 'line':line, 'start':lines[int(line) - 1][:30]})
|
||||
continue
|
||||
m = re.search(r'\s*unknown element "(.*?)"', message)
|
||||
if m:
|
||||
display_errors.append(_('"<%(tag)s>" on line %(line)s is an invalid tag. (Line starts with "%(start)s".)') % \
|
||||
{'tag':m.group(1), 'line':line, 'start':lines[int(line) - 1][:30]})
|
||||
continue
|
||||
if message.strip() == 'required attributes missing':
|
||||
display_errors.append(_('A tag on line %(line)s is missing one or more required attributes. (Line starts with "%(start)s".)') % \
|
||||
{'line':line, 'start':lines[int(line) - 1][:30]})
|
||||
continue
|
||||
m = re.search(r'\s*bad value for attribute "(.*?)"', message)
|
||||
if m:
|
||||
display_errors.append(_('The "%(attr)s" attribute on line %(line)s has an invalid value. (Line starts with "%(start)s".)') % \
|
||||
{'attr':m.group(1), 'line':line, 'start':lines[int(line) - 1][:30]})
|
||||
continue
|
||||
# Failing all those checks, use the default error message.
|
||||
display_error = 'Line %s: %s [%s]' % (line, message, level.strip())
|
||||
display_errors.append(display_error)
|
||||
if len(display_errors) > 0:
|
||||
raise ValidationError, display_errors
|
||||
Reference in New Issue
Block a user