Django

Code

Ticket #6845: 6845-against-7625.diff

File 6845-against-7625.diff, 136.4 kB (added by Honza_Kral, 2 years ago)

new working patch

  • a/AUTHORS

    old new  
    387387    ymasuda@ethercube.com 
    388388    Jarek Zgoda <jarek.zgoda@gmail.com> 
    389389    Cheng Zhang 
     390    Honza Kral <Honza.Kral@gmail.com> 
    390391 
    391392A big THANK YOU goes to: 
    392393 
  • a/django/contrib/admin/views/template.py

    old new  
    11from django.contrib.admin.views.decorators import staff_member_required 
    2 from django.core import validators 
     2from django.core import validation 
    33from django import template, oldforms 
    44from django.template import loader 
    55from django.shortcuts import render_to_response 
     
    6969            error = e 
    7070        template.builtins.remove(register) 
    7171        if error: 
    72             raise validators.ValidationError, e.args 
     72            raise validation.ValidationError, e.args 
  • a/django/contrib/auth/forms.py

    old new  
    22from django.contrib.auth import authenticate 
    33from django.contrib.sites.models import Site 
    44from django.template import Context, loader 
    5 from django.core import validators 
     5from django.oldforms import validators 
    66from django import oldforms 
    77from django.utils.translation import ugettext as _ 
    88 
  • a/django/contrib/auth/management/commands/createsuperuser.py

    old new  
    3939            if not RE_VALID_USERNAME.match(username): 
    4040                raise CommandError("Invalid username. Use only letters, digits, and underscores") 
    4141            try: 
    42                 validators.isValidEmail(email, None
     42                validators.validate_email(email
    4343            except validators.ValidationError: 
    4444                raise CommandError("Invalid email address.") 
    4545 
  • a/django/contrib/auth/models.py

    old new  
    11from django.contrib import auth 
    2 from django.core import validators 
     2from django.oldforms import validators 
    33from django.core.exceptions import ImproperlyConfigured 
    44from django.db import models 
    55from django.db.models.manager import EmptyManager 
     
    135135 
    136136    Username and password are required. Other fields are optional. 
    137137    """ 
    138     username = models.CharField(_('username'), max_length=30, unique=True, validator_list=[validators.isAlphaNumeric], help_text=_("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores).")) 
     138    username = models.CharField(_('username'), max_length=30, unique=True, validators=[validators.isAlphaNumeric], help_text=_("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores).")) 
    139139    first_name = models.CharField(_('first name'), max_length=30, blank=True) 
    140140    last_name = models.CharField(_('last name'), max_length=30, blank=True) 
    141141    email = models.EmailField(_('e-mail address'), blank=True) 
  • a/django/contrib/comments/views/comments.py

    old new  
    1 from django.core import validators 
     1from django.oldforms import validators 
    22from django import oldforms 
    33from django.core.mail import mail_admins, mail_managers 
    44from django.http import Http404 
  • a/django/contrib/flatpages/models.py

    old new  
    1 from django.core import validators 
     1from django.oldforms import validators 
    22from django.db import models 
    33from django.contrib.sites.models import Site 
    44from django.utils.translation import ugettext_lazy as _ 
    55 
    66 
    77class FlatPage(models.Model): 
    8     url = models.CharField(_('URL'), max_length=100, validator_list=[validators.isAlphaNumericURL], db_index=True, 
     8    url = models.CharField(_('URL'), max_length=100, validators=[validators.isAlphaNumericURL], db_index=True, 
    99        help_text=_("Example: '/about/contact/'. Make sure to have leading and trailing slashes.")) 
    1010    title = models.CharField(_('title'), max_length=200) 
    1111    content = models.TextField(_('content')) 
  • a/django/contrib/localflavor/br/forms.py

    old new  
    3131    } 
    3232 
    3333    def clean(self, value): 
    34         super(BRPhoneNumberField, self).clean(value) 
     34        value = super(BRPhoneNumberField, self).clean(value) 
    3535        if value in EMPTY_VALUES: 
    3636            return u'' 
    37         value = re.sub('(\(|\)|\s+)', '', smart_unicode(value)
     37        value = re.sub('(\(|\)|\s+)', '', value
    3838        m = phone_digits_re.search(value) 
    3939        if m: 
    4040            return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3)) 
  • a/django/contrib/localflavor/fi/forms.py

    old new  
    2929    } 
    3030 
    3131    def clean(self, value): 
    32         super(FISocialSecurityNumber, self).clean(value) 
    33         if value in EMPTY_VALUES: 
     32        # changed not to throw UnicodeDecode error when passed invalid data 
     33        # I think this SHOULD call super.clean, which would mean ^^^ 
     34        if self.required and value in EMPTY_VALUES: 
     35             raise ValidationError(self.error_messages['required']) 
     36        elif value in EMPTY_VALUES: 
    3437            return u'' 
    3538 
    3639        checkmarks = "0123456789ABCDEFHJKLMNPRSTUVWXY" 
  • a/django/contrib/localflavor/jp/forms.py

    old new  
    22JP-specific Form helpers 
    33""" 
    44 
    5 from django.core import validators 
    65from django.newforms import ValidationError 
    76from django.utils.translation import ugettext 
    87from django.newforms.fields import RegexField, Select 
  • /dev/null

    old new  
     1from django.utils.encoding import smart_unicode, StrAndUnicode, force_unicode 
     2from django.utils.safestring import mark_safe 
     3 
     4NON_FIELD_ERRORS = '__all__' 
     5 
     6class ErrorList(list, StrAndUnicode): 
     7    """ 
     8    A collection of errors that knows how to display itself in various formats. 
     9    """ 
     10    def __unicode__(self): 
     11        return self.as_ul() 
     12 
     13    def as_ul(self): 
     14        if not self: return u'' 
     15        return mark_safe(u'<ul class="errorlist">%s</ul>' 
     16                % ''.join([u'<li>%s</li>' % force_unicode(e) for e in self])) 
     17 
     18    def as_text(self): 
     19        if not self: return u'' 
     20        return u'\n'.join([u'* %s' % force_unicode(e) for e in self]) 
     21 
     22    def __repr__(self): 
     23        return repr([force_unicode(e) for e in self]) 
     24 
     25class ValidationError(Exception): 
     26    def __init__(self, message): 
     27        """ 
     28        ValidationError can be passed any object that can be printed (usually 
     29        a string) or a list of objects. 
     30        """ 
     31        if hasattr(message, '__iter__'): 
     32            self.messages = ErrorList([smart_unicode(msg) for msg in message]) 
     33        else: 
     34            message = smart_unicode(message) 
     35            self.messages = ErrorList([message]) 
     36 
     37        if isinstance(message, dict): 
     38            self.message_dict = message 
     39 
     40    def __str__(self): 
     41        # This is needed because, without a __str__(), printing an exception 
     42        # instance would result in this: 
     43        # AttributeError: ValidationError instance has no attribute 'args' 
     44        # See http://www.python.org/doc/current/tut/node10.html#handling 
     45        if hasattr(self, 'message_dict'): 
     46            return repr(self.message_dict) 
     47        return repr(self.messages) 
     48 
     49class TypeCoercionError(ValidationError): 
     50    pass 
  • a/django/core/validators.py

    old new  
    11""" 
    22A library of validators that return None and raise ValidationError when the 
    33provided data isn't valid. 
    4  
    5 Validators may be callable classes, and they may have an 'always_test' 
    6 attribute. If an 'always_test' attribute exists (regardless of value), the 
    7 validator will *always* be run, regardless of whether its associated 
    8 form field is required. 
    94""" 
    105 
    11 import urllib2 
    126import re 
    13 try: 
    14     from decimal import Decimal, DecimalException 
    15 except ImportError: 
    16     from django.utils._decimal import Decimal, DecimalException    # Python 2.3 
     7import urllib2 
    178 
    189from django.conf import settings 
    19 from django.utils.translation import ugettext as _, ugettext_lazy, ungettext 
    20 from django.utils.functional import Promise, lazy 
    21 from django.utils.encoding import force_unicode, smart_str 
     10from django.utils.translation import ugettext as _ 
     11from django.core.validation import ValidationError 
     12 
     13def regexp_validator(regexp, message): 
     14    if isinstance(regexp, basestring): 
     15        regexp = re.compile(regexp) 
     16 
     17    def _regexp_validator(value): 
     18        if not regexp.search(value): 
     19            raise ValidationError, _(message) 
     20    return _regexp_validator 
    2221 
    23 _datere = r'\d{4}-\d{1,2}-\d{1,2}' 
    24 _timere = r'(?:[01]?[0-9]|2[0-3]):[0-5][0-9](?::[0-5][0-9])?' 
    25 alnum_re = re.compile(r'^\w+$') 
    26 alnumurl_re = re.compile(r'^[-\w/]+$') 
    27 ansi_date_re = re.compile('^%s$' % _datere) 
    28 ansi_time_re = re.compile('^%s$' % _timere) 
    29 ansi_datetime_re = re.compile('^%s %s$' % (_datere, _timere)) 
    3022email_re = re.compile( 
    3123    r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*"  # dot-atom 
    3224    r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"' # quoted-string 
    3325    r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE)  # domain 
    34 integer_re = re.compile(r'^-?\d+$') 
    35 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}$') 
    36 phone_re = re.compile(r'^[A-PR-Y0-9]{3}-[A-PR-Y0-9]{3}-[A-PR-Y0-9]{4}$', re.IGNORECASE) 
    37 slug_re = re.compile(r'^[-\w]+$') 
    38 url_re = re.compile(r'^https?://\S+$') 
     26validate_email = regexp_validator(email_re, 'Enter a valid e-mail address.') 
    3927 
    40 lazy_inter = lazy(lambda a,b: force_unicode(a) % b, unicode) 
     28validate_ip_address4 = regexp_validator( 
     29        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}$'), 
     30        "Please enter a valid IP address." 
     31    ) 
    4132 
    42 class ValidationError(Exception): 
    43     def __init__(self, message): 
    44         "ValidationError can be passed a string or a list." 
    45         if isinstance(message, list): 
    46             self.messages = [force_unicode(msg) for msg in message] 
    47         else: 
    48             assert isinstance(message, (basestring, Promise)), ("%s should be a string" % repr(message)) 
    49             self.messages = [force_unicode(message)] 
     33validate_phone_number = regexp_validator( 
     34        re.compile(r'^[A-PR-Y0-9]{3}-[A-PR-Y0-9]{3}-[A-PR-Y0-9]{4}$', re.IGNORECASE), 
     35        _('Phone numbers must be in XXX-XXX-XXXX format.') 
     36    ) 
    5037 
    51     def __str__(self): 
    52         # This is needed because, without a __str__(), printing an exception 
    53         # instance would result in this: 
    54         # AttributeError: ValidationError instance has no attribute 'args' 
    55         # See http://www.python.org/doc/current/tut/node10.html#handling 
    56         return str(self.messages) 
     38validate_slug = regexp_validator( 
     39        re.compile(r'^[-\w]+$'), 
     40        "This value must contain only letters, numbers, underscores or hyphens." 
     41    ) 
    5742 
    58 class CriticalValidationError(Exception): 
    59     def __init__(self, message): 
    60         "ValidationError can be passed a string or a list." 
    61         if isinstance(message, list): 
    62             self.messages = [force_unicode(msg) for msg in message] 
    63         else: 
    64             assert isinstance(message, (basestring, Promise)), ("'%s' should be a string" % message) 
    65             self.messages = [force_unicode(message)] 
    66  
    67     def __str__(self): 
    68         return str(self.messages) 
    69  
    70 def isAlphaNumeric(field_data, all_data): 
    71     if not alnum_re.search(field_data): 
    72         raise ValidationError, _("This value must contain only letters, numbers and underscores.") 
    73  
    74 def isAlphaNumericURL(field_data, all_data): 
    75     if not alnumurl_re.search(field_data): 
    76         raise ValidationError, _("This value must contain only letters, numbers, underscores, dashes or slashes.") 
    77  
    78 def isSlug(field_data, all_data): 
    79     if not slug_re.search(field_data): 
    80         raise ValidationError, _("This value must contain only letters, numbers, underscores or hyphens.") 
    81  
    82 def isLowerCase(field_data, all_data): 
    83     if field_data.lower() != field_data: 
    84         raise ValidationError, _("Uppercase letters are not allowed here.") 
     43_datere = r'\d{4}-\d{1,2}-\d{1,2}' 
     44validate_ansi_date = regexp_validator( 
     45        re.compile('^%s$' % _datere), 
     46        _('Enter a valid date in YYYY-MM-DD format.') 
     47    ) 
    8548 
    86 def isUpperCase(field_data, all_data): 
    87     if field_data.upper() != field_data: 
    88         raise ValidationError, _("Lowercase letters are not allowed here.") 
     49_timere = r'(?:[01]?[0-9]|2[0-3]):[0-5][0-9](?::[0-5][0-9])?' 
     50validate_ansi_time = regexp_validator( 
     51        re.compile('^%s$' % _timere), 
     52        _('Enter a valid time in HH:MM format.') 
     53    ) 
    8954 
    90 def isCommaSeparatedIntegerList(field_data, all_data): 
    91     for supposed_int in field_data.split(','): 
    92         try: 
    93             int(supposed_int) 
    94         except ValueError: 
    95             raise ValidationError, _("Enter only digits separated by commas.") 
     55validate_ansi_datetime = regexp_validator( 
     56        re.compile('^%s %s$' % (_datere, _timere)), 
     57        _('Enter a valid date/time in YYYY-MM-DD HH:MM format.') 
     58    ) 
    9659 
    97 def isCommaSeparatedEmailList(field_data, all_data): 
     60def validate_comma_separated_email_list(value): 
    9861    """ 
    99     Checks that field_data is a string of e-mail addresses separated by commas. 
    100     Blank field_data values will not throw a validation error, and whitespace 
     62    Checks that value is a string of e-mail addresses separated by commas. 
     63    Blank value values will not throw a validation error, and whitespace 
    10164    is allowed around the commas. 
    10265    """ 
    103     for supposed_email in field_data.split(','): 
     66    for supposed_email in value.split(','): 
    10467        try: 
    105             isValidEmail(supposed_email.strip(), ''
     68            validate_email(supposed_email.strip()
    10669        except ValidationError: 
    10770            raise ValidationError, _("Enter valid e-mail addresses separated by commas.") 
    10871 
    109 def isValidIPAddress4(field_data, all_data): 
    110     if not ip4_re.search(field_data): 
    111         raise ValidationError, _("Please enter a valid IP address.") 
    112  
    113 def isNotEmpty(field_data, all_data): 
    114     if field_data.strip() == '': 
    115         raise ValidationError, _("Empty values are not allowed here.") 
    116  
    117 def isOnlyDigits(field_data, all_data): 
    118     if not field_data.isdigit(): 
    119         raise ValidationError, _("Non-numeric characters aren't allowed here.") 
    120  
    121 def isNotOnlyDigits(field_data, all_data): 
    122     if field_data.isdigit(): 
    123         raise ValidationError, _("This value can't be comprised solely of digits.") 
    124  
    125 def isInteger(field_data, all_data): 
    126     # This differs from isOnlyDigits because this accepts the negative sign 
    127     if not integer_re.search(field_data): 
    128         raise ValidationError, _("Enter a whole number.") 
    129  
    130 def isOnlyLetters(field_data, all_data): 
    131     if not field_data.isalpha(): 
    132         raise ValidationError, _("Only alphabetical characters are allowed here.") 
    133  
    134 def _isValidDate(date_string): 
    135     """ 
    136     A helper function used by isValidANSIDate and isValidANSIDatetime to 
    137     check if the date is valid.  The date string is assumed to already be in 
    138     YYYY-MM-DD format. 
    139     """ 
    140     from datetime import date 
    141     # Could use time.strptime here and catch errors, but datetime.date below 
    142     # produces much friendlier error messages. 
    143     year, month, day = map(int, date_string.split('-')) 
    144     # This check is needed because strftime is used when saving the date 
    145     # value to the database, and strftime requires that the year be >=1900. 
    146     if year < 1900: 
    147         raise ValidationError, _('Year must be 1900 or later.') 
    148     try: 
    149         date(year, month, day) 
    150     except ValueError, e: 
    151         msg = _('Invalid date: %s') % _(str(e)) 
    152         raise ValidationError, msg 
    153  
    154 def isValidANSIDate(field_data, all_data): 
    155     if not ansi_date_re.search(field_data): 
    156         raise ValidationError, _('Enter a valid date in YYYY-MM-DD format.') 
    157     _isValidDate(field_data) 
    158  
    159 def isValidANSITime(field_data, all_data): 
    160     if not ansi_time_re.search(field_data): 
    161         raise ValidationError, _('Enter a valid time in HH:MM format.') 
    162  
    163 def isValidANSIDatetime(field_data, all_data): 
    164     if not ansi_datetime_re.search(field_data): 
    165         raise ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM format.') 
    166     _isValidDate(field_data.split()[0]) 
    167  
    168 def isValidEmail(field_data, all_data): 
    169     if not email_re.search(field_data): 
    170         raise ValidationError, _('Enter a valid e-mail address.') 
    171  
    172 def isValidImage(field_data, all_data): 
    173     """ 
    174     Checks that the file-upload field data contains a valid image (GIF, JPG, 
    175     PNG, possibly others -- whatever the Python Imaging Library supports). 
    176     """ 
    177     from PIL import Image 
    178     from cStringIO import StringIO 
    179     try: 
    180         content = field_data['content'] 
    181     except TypeError: 
    182         raise ValidationError, _("No file was submitted. Check the encoding type on the form.") 
    183     try: 
    184         # load() is the only method that can spot a truncated JPEG, 
    185         #  but it cannot be called sanely after verify() 
    186         trial_image = Image.open(StringIO(content)) 
    187         trial_image.load() 
    188         # verify() is the only method that can spot a corrupt PNG, 
    189         #  but it must be called immediately after the constructor 
    190         trial_image = Image.open(StringIO(content)) 
    191         trial_image.verify() 
    192     except Exception: # Python Imaging Library doesn't recognize it as an image 
    193         raise ValidationError, _("Upload a valid image. The file you uploaded was either not an image or a corrupted image.") 
    194  
    195 def isValidImageURL(field_data, all_data): 
    196     uc = URLMimeTypeCheck(('image/jpeg', 'image/gif', 'image/png')) 
    197     try: 
    198         uc(field_data, all_data) 
    199     except URLMimeTypeCheck.InvalidContentType: 
    200         raise ValidationError, _("The URL %s does not point to a valid image.") % field_data 
    201  
    202 def isValidPhone(field_data, all_data): 
    203     if not phone_re.search(field_data): 
    204         raise ValidationError, _('Phone numbers must be in XXX-XXX-XXXX format. "%s" is invalid.') % field_data 
    205  
    206 def isValidQuicktimeVideoURL(field_data, all_data): 
    207     "Checks that the given URL is a video that can be played by QuickTime (qt, mpeg)" 
    208     uc = URLMimeTypeCheck(('video/quicktime', 'video/mpeg',)) 
    209     try: 
    210         uc(field_data, all_data) 
    211     except URLMimeTypeCheck.InvalidContentType: 
    212         raise ValidationError, _("The URL %s does not point to a valid QuickTime video.") % field_data 
    213  
    214 def isValidURL(field_data, all_data): 
    215     if not url_re.search(field_data): 
    216         raise ValidationError, _("A valid URL is required.") 
    217  
    218 def isValidHTML(field_data, all_data): 
    219     import urllib, urllib2 
    220     try: 
    221         u = urllib2.urlopen('http://validator.w3.org/check', urllib.urlencode({'fragment': field_data, 'output': 'xml'})) 
    222     except: 
    223         # Validator or Internet connection is unavailable. Fail silently. 
    224         return 
    225     html_is_valid = (u.headers.get('x-w3c-validator-status', 'Invalid') == 'Valid') 
    226     if html_is_valid: 
    227         return 
    228     from xml.dom.minidom import parseString 
    229     error_messages = [e.firstChild.wholeText for e in parseString(u.read()).getElementsByTagName('messages')[0].getElementsByTagName('msg')] 
    230     raise ValidationError, _("Valid HTML is required. Specific errors are:\n%s") % "\n".join(error_messages) 
    231  
    232 def isWellFormedXml(field_data, all_data): 
    233     from xml.dom.minidom import parseString 
    234     try: 
    235         parseString(field_data) 
    236     except Exception, e: # Naked except because we're not sure what will be thrown 
    237         raise ValidationError, _("Badly formed XML: %s") % str(e) 
    238  
    239 def isWellFormedXmlFragment(field_data, all_data): 
    240     isWellFormedXml('<root>%s</root>' % field_data, all_data) 
    241  
    242 def isExistingURL(field_data, all_data): 
     72def validate_existing_url(value): 
    24373    try: 
    24474        headers = { 
    24575            "Accept" : "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5", 
     
    24878            "Connection" : "close", 
    24979            "User-Agent": settings.URL_VALIDATOR_USER_AGENT 
    25080            } 
    251         req = urllib2.Request(field_data,None, headers) 
     81        req = urllib2.Request(value,None, headers) 
    25282        u = urllib2.urlopen(req) 
    25383    except ValueError: 
    254         raise ValidationError, _("Invalid URL: %s") % field_data 
     84        raise ValidationError, _("Invalid URL: %s") % value 
    25585    except urllib2.HTTPError, e: 
    25686        # 401s are valid; they just mean authorization is required. 
    25787        # 301 and 302 are redirects; they just mean look somewhere else. 
    25888        if str(e.code) not in ('401','301','302'): 
    259             raise ValidationError, _("The URL %s is a broken link.") % field_data 
     89            raise ValidationError, _("The URL %s is a broken link.") % value 
    26090    except: # urllib2.URLError, httplib.InvalidURL, etc. 
    261         raise ValidationError, _("The URL %s is a broken link.") % field_data 
    262  
    263 def isValidUSState(field_data, all_data): 
    264     "Checks that the given string is a valid two-letter U.S. state abbreviation" 
    265     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'] 
    266     if field_data.upper() not in states: 
    267         raise ValidationError, _("Enter a valid U.S. state abbreviation.") 
    268  
    269 def hasNoProfanities(field_data, all_data): 
    270     """ 
    271     Checks that the given string has no profanities in it. This does a simple 
    272     check for whether each profanity exists within the string, so 'fuck' will 
    273     catch 'motherfucker' as well. Raises a ValidationError such as: 
    274         Watch your mouth! The words "f--k" and "s--t" are not allowed here. 
    275     """ 
    276     field_data = field_data.lower() # normalize 
    277     words_seen = [w for w in settings.PROFANITIES_LIST if w in field_data] 
    278     if words_seen: 
    279         from django.utils.text import get_text_list 
    280         plural = len(words_seen) 
    281         raise ValidationError, ungettext("Watch your mouth! The word %s is not allowed here.", 
    282             "Watch your mouth! The words %s are not allowed here.", plural) % \ 
    283             get_text_list(['"%s%s%s"' % (i[0], '-'*(len(i)-2), i[-1]) for i in words_seen], _('and')) 
    284  
    285 class AlwaysMatchesOtherField(object): 
    286     def __init__(self, other_field_name, error_message=None): 
    287         self.other = other_field_name 
    288         self.error_message = error_message or lazy_inter(ugettext_lazy("This field must match the '%s' field."), self.other) 
    289         self.always_test = True 
    290  
    291     def __call__(self, field_data, all_data): 
    292         if field_data != all_data[self.other]: 
    293             raise ValidationError, self.error_message 
    294  
    295 class ValidateIfOtherFieldEquals(object): 
    296     def __init__(self, other_field, other_value, validator_list): 
    297         self.other_field, self.other_value = other_field, other_value 
    298         self.validator_list = validator_list 
    299         self.always_test = True 
    300  
    301     def __call__(self, field_data, all_data): 
    302         if self.other_field in all_data and all_data[self.other_field] == self.other_value: 
    303             for v in self.validator_list: 
    304                 v(field_data, all_data) 
    305  
    306 class RequiredIfOtherFieldNotGiven(object): 
    307     def __init__(self, other_field_name, error_message=ugettext_lazy("Please enter something for at least one field.")): 
    308         self.other, self.error_message = other_field_name, error_message 
    309         self.always_test = True 
    310  
    311     def __call__(self, field_data, all_data): 
    312         if not all_data.get(self.other, False) and not field_data: 
    313             raise ValidationError, self.error_message 
    314  
    315 class RequiredIfOtherFieldsGiven(object): 
    316     def __init__(self, other_field_names, error_message=ugettext_lazy("Please enter both fields or leave them both empty.")): 
    317         self.other, self.error_message = other_field_names, error_message 
    318         self.always_test = True 
    319  
    320     def __call__(self, field_data, all_data): 
    321         for field in self.other: 
    322             if all_data.get(field, False) and not field_data: 
    323                 raise ValidationError, self.error_message 
    324  
    325 class RequiredIfOtherFieldGiven(RequiredIfOtherFieldsGiven): 
    326     "Like RequiredIfOtherFieldsGiven, but takes a single field name instead of a list." 
    327     def __init__(self, other_field_name, error_message=ugettext_lazy("Please enter both fields or leave them both empty.")): 
    328         RequiredIfOtherFieldsGiven.__init__(self, [other_field_name], error_message) 
    329  
    330 class RequiredIfOtherFieldEquals(object): 
    331     def __init__(self, other_field, other_value, error_message=None, other_label=None): 
    332         self.other_field = other_field 
    333         self.other_value = other_value 
    334         other_label = other_label or other_value 
    335         self.error_message = error_message or lazy_inter(ugettext_lazy("This field must be given if %(field)s is %(value)s"), { 
    336             'field': other_field, 'value': other_label}) 
    337         self.always_test = True 
    338  
    339     def __call__(self, field_data, all_data): 
    340         if self.other_field in all_data and all_data[self.other_field] == self.other_value and not field_data: 
    341             raise ValidationError(self.error_message) 
    342  
    343 class RequiredIfOtherFieldDoesNotEqual(object): 
    344     def __init__(self, other_field, other_value, other_label=None, error_message=None): 
    345         self.other_field = other_field 
    346         self.other_value = other_value 
    347         other_label = other_label or other_value 
    348         self.error_message = error_message or lazy_inter(ugettext_lazy("This field must be given if %(field)s is not %(value)s"), { 
    349             'field': other_field, 'value': other_label}) 
    350         self.always_test = True 
    351  
    352     def __call__(self, field_data, all_data): 
    353         if self.other_field in all_data and all_data[self.other_field] != self.other_value and not field_data: 
    354             raise ValidationError(self.error_message) 
    355  
    356 class IsLessThanOtherField(object): 
    357     def __init__(self, other_field_name, error_message): 
    358         self.other, self.error_message = other_field_name, error_message 
    359  
    360     def __call__(self, field_data, all_data): 
    361         if field_data > all_data[self.other]: 
    362             raise ValidationError, self.error_message 
    363  
    364 class UniqueAmongstFieldsWithPrefix(object): 
    365     def __init__(self, field_name, prefix, error_message): 
    366         self.field_name, self.prefix = field_name, prefix 
    367         self.error_message = error_message or ugettext_lazy("Duplicate values are not allowed.") 
    368  
    369     def __call__(self, field_data, all_data): 
    370         for field_name, value in all_data.items(): 
    371             if field_name != self.field_name and value == field_data: 
    372                 raise ValidationError, self.error_message 
    373  
    374 class NumberIsInRange(object): 
    375     """ 
    376     Validator that tests if a value is in a range (inclusive). 
    377     """ 
    378     def __init__(self, lower=None, upper=None, error_message=''): 
    379         self.lower, self.upper = lower, upper 
    380         if not error_message: 
    381             if lower and upper: 
    382                  self.error_message = _("This value must be between %(lower)s and %(upper)s.") % {'lower': lower, 'upper': upper} 
    383             elif lower: 
    384                 self.error_message = _("This value must be at least %s.") % lower 
    385             elif upper: 
    386                 self.error_message = _("This value must be no more than %s.") % upper 
    387         else: 
    388             self.error_message = error_message 
    389  
    390     def __call__(self, field_data, all_data): 
    391         # Try to make the value numeric. If this fails, we assume another 
    392         # validator will catch the problem. 
    393         try: 
    394             val = float(field_data) 
    395         except ValueError: 
    396             return 
    397  
    398         # Now validate 
    399         if self.lower and self.upper and (val < self.lower or val > self.upper): 
    400             raise ValidationError(self.error_message) 
    401         elif self.lower and val < self.lower: 
    402             raise ValidationError(self.error_message) 
    403         elif self.upper and val > self.upper: 
    404             raise ValidationError(self.error_message) 
    405  
    406 class IsAPowerOf(object): 
    407     """ 
    408     Usage: If you create an instance of the IsPowerOf validator: 
    409         v = IsAPowerOf(2) 
    410      
    411     The following calls will succeed: 
    412         v(4, None)  
    413         v(8, None) 
    414         v(16, None) 
    415      
    416     But this call: 
    417         v(17, None) 
    418     will raise "django.core.validators.ValidationError: ['This value must be a power of 2.']" 
    419     """ 
    420     def __init__(self, power_of): 
    421         self.power_of = power_of 
    422  
    423     def __call__(self, field_data, all_data): 
    424         from math import log 
    425         val = log(int(field_data)) / log(self.power_of) 
    426         if val != int(val): 
    427             raise ValidationError, _("This value must be a power of %s.") % self.power_of 
    428  
    429 class IsValidDecimal(object): 
    430     def __init__(self, max_digits, decimal_places): 
    431         self.max_digits, self.decimal_places = max_digits, decimal_places 
    432  
    433     def __call__(self, field_data, all_data): 
    434         try: 
    435             val = Decimal(field_data) 
    436         except DecimalException: 
    437             raise ValidationError, _("Please enter a valid decimal number.") 
    438  
    439         pieces = str(val).lstrip("-").split('.') 
    440         decimals = (len(pieces) == 2) and len(pieces[1]) or 0 
    441         digits = len(pieces[0]) 
    442  
    443         if digits + decimals > self.max_digits: 
    444             raise ValidationError, ungettext("Please enter a valid decimal number with at most %s total digit.", 
    445                 "Please enter a valid decimal number with at most %s total digits.", self.max_digits) % self.max_digits 
    446         if digits > (self.max_digits - self.decimal_places): 
    447             raise ValidationError, ungettext( "Please enter a valid decimal number with a whole part of at most %s digit.", 
    448                 "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) 
    449         if decimals > self.decimal_places: 
    450             raise ValidationError, ungettext("Please enter a valid decimal number with at most %s decimal place.", 
    451                 "Please enter a valid decimal number with at most %s decimal places.", self.decimal_places) % self.decimal_places 
    452  
    453 def isValidFloat(field_data, all_data): 
    454     data = smart_str(field_data) 
    455     try: 
    456         float(data) 
    457     except ValueError: 
    458         raise ValidationError, _("Please enter a valid floating point number.") 
    459  
    460 class HasAllowableSize(object): 
    461     """ 
    462     Checks that the file-upload field data is a certain size. min_size and 
    463     max_size are measurements in bytes. 
    464     """ 
    465     def __init__(self, min_size=None, max_size=None, min_error_message=None, max_error_message=None): 
    466         self.min_size, self.max_size = min_size, max_size 
    467         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) 
    468         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) 
    469  
    470     def __call__(self, field_data, all_data): 
    471         try: 
    472             content = field_data['content'] 
    473         except TypeError: 
    474             raise ValidationError, ugettext_lazy("No file was submitted. Check the encoding type on the form.") 
    475         if self.min_size is not None and len(content) < self.min_size: 
    476             raise ValidationError, self.min_error_message 
    477         if self.max_size is not None and len(content) > self.max_size: 
    478             raise ValidationError, self.max_error_message 
    479  
    480 class MatchesRegularExpression(object): 
    481     """ 
    482     Checks that the field matches the given regular-expression. The regex 
    483     should be in string format, not already compiled. 
    484     """ 
    485     def __init__(self, regexp, error_message=ugettext_lazy("The format for this field is wrong.")): 
    486         self.regexp = re.compile(regexp) 
    487         self.error_message = error_message 
    488  
    489     def __call__(self, field_data, all_data): 
    490         if not self.regexp.search(field_data): 
    491             raise ValidationError(self.error_message) 
    492  
    493 class AnyValidator(object): 
    494     """ 
    495     This validator tries all given validators. If any one of them succeeds, 
    496     validation passes. If none of them succeeds, the given message is thrown 
    497     as a validation error. The message is rather unspecific, so it's best to 
    498     specify one on instantiation. 
    499     """ 
    500     def __init__(self, validator_list=None, error_message=ugettext_lazy("This field is invalid.")): 
    501         if validator_list is None: validator_list = [] 
    502         self.validator_list = validator_list 
    503         self.error_message = error_message 
    504         for v in validator_list: 
    505             if hasattr(v, 'always_test'): 
    506                 self.always_test = True 
    507  
    508     def __call__(self, field_data, all_data): 
    509         for v in self.validator_list: 
    510             try: 
    511                 v(field_data, all_data) 
    512                 return 
    513             except ValidationError, e: 
    514                 pass 
    515         raise ValidationError(self.error_message) 
     91        raise ValidationError, _("The URL %s is a broken link.") % value 
    51692 
    51793class URLMimeTypeCheck(object): 
    51894    "Checks that the provided URL points to a document with a listed mime type" 
     
    524100    def __init__(self, mime_type_list): 
    525101        self.mime_type_list = mime_type_list 
    526102 
    527     def __call__(self, field_data, all_data): 
    528         import urllib2 
     103    def __call__(self, value): 
     104        validate_existing_url(value) 
    529105        try: 
    530             isValidURL(field_data, all_data) 
    531         except ValidationError: 
    532             raise 
    533         try: 
    534             info = urllib2.urlopen(field_data).info() 
     106            info = urllib2.urlopen(value).info() 
    535107        except (urllib2.HTTPError, urllib2.URLError): 
    536             raise URLMimeTypeCheck.CouldNotRetrieve, _("Could not retrieve anything from %s.") % field_data 
     108            raise URLMimeTypeCheck.CouldNotRetrieve, _("Could not retrieve anything from %s.") % value 
    537109        content_type = info['content-type'] 
    538110        if content_type not in self.mime_type_list: 
    539111            raise URLMimeTypeCheck.InvalidContentType, _("The URL %(url)s returned the invalid Content-Type header '%(contenttype)s'.") % { 
    540                 'url': field_data, 'contenttype': content_type} 
     112                'url': value, 'contenttype': content_type} 
    541113 
    542 class RelaxNGCompact(object): 
    543     "Validate against a Relax NG compact schema" 
    544     def __init__(self, schema_path, additional_root_element=None): 
    545         self.schema_path = schema_path 
    546         self.additional_root_element = additional_root_element 
     114def validate_imaga_url(value): 
     115    uc = URLMimeTypeCheck(('image/jpeg', 'image/gif', 'image/png')) 
     116    try: 
     117        uc(value) 
     118    except URLMimeTypeCheck.InvalidContentType: 
     119        raise ValidationError, _("The URL %s does not point to a valid image.") % value 
    547120 
    548     def __call__(self, field_data, all_data): 
    549         import os, tempfile 
    550         if self.additional_root_element: 
    551             field_data = '<%(are)s>%(data)s\n</%(are)s>' % { 
    552                 'are': self.additional_root_element, 
    553                 'data': field_data 
    554             } 
    555         filename = tempfile.mktemp() # Insecure, but nothing else worked 
    556         fp = open(filename, 'w') 
    557         fp.write(field_data) 
    558         fp.close() 
    559         if not os.path.exists(settings.JING_PATH): 
    560             raise Exception, "%s not found!" % settings.JING_PATH 
    561         p = os.popen('%s -c %s %s' % (settings.JING_PATH, self.schema_path, filename)) 
    562         errors = [line.strip() for line in p.readlines()] 
    563         p.close() 
    564         os.unlink(filename) 
    565         display_errors = [] 
    566         lines = field_data.split('\n') 
    567         for error in errors: 
    568             ignored, line, level, message = error.split(':', 3) 
    569             # Scrape the Jing error messages to reword them more nicely. 
    570             m = re.search(r'Expected "(.*?)" to terminate element starting on line (\d+)', message) 
    571             if m: 
    572                 display_errors.append(_('Please close the unclosed %(tag)s tag from line %(line)s. (Line starts with "%(start)s".)') % \ 
    573                     {'tag':m.group(1).replace('/', ''), 'line':m.group(2), 'start':lines[int(m.group(2)) - 1][:30]}) 
    574                 continue 
    575             if message.strip() == 'text not allowed here': 
    576                 display_errors.append(_('Some text starting on line %(line)s is not allowed in that context. (Line starts with "%(start)s".)') % \ 
    577                     {'line':line, 'start':lines[int(line) - 1][:30]}) 
    578                 continue 
    579             m = re.search(r'\s*attribute "(.*?)" not allowed at this point; ignored', message) 
    580             if m: 
    581                 display_errors.append(_('"%(attr)s" on line %(line)s is an invalid attribute. (Line starts with "%(start)s".)') % \ 
    582                     {'attr':m.group(1), 'line':line, 'start':lines[int(line) - 1][:30]}) 
    583                 continue 
    584             m = re.search(r'\s*unknown element "(.*?)"', message) 
    585             if m: 
    586                 display_errors.append(_('"<%(tag)s>" on line %(line)s is an invalid tag. (Line starts with "%(start)s".)') % \ 
    587                     {'tag':m.group(1), 'line':line, 'start':lines[int(line) - 1][:30]}) 
    588                 continue 
    589             if message.strip() == 'required attributes missing': 
    590                 display_errors.append(_('A tag on line %(line)s is missing one or more required attributes. (Line starts with "%(start)s".)') % \ 
    591                     {'line':line, 'start':lines[int(line) - 1][:30]}) 
    592                 continue 
    593             m = re.search(r'\s*bad value for attribute "(.*?)"', message) 
    594             if m: 
    595                 display_errors.append(_('The "%(attr)s" attribute on line %(line)s has an invalid value. (Line starts with "%(start)s".)') % \ 
    596                     {'attr':m.group(1), 'line':line, 'start':lines[int(line) - 1][:30]}) 
    597                 continue 
    598             # Failing all those checks, use the default error message. 
    599             display_error = 'Line %s: %s [%s]' % (line, message, level.strip()) 
    600             display_errors.append(display_error) 
    601         if len(display_errors) > 0: 
    602             raise ValidationError, display_errors 
     121def validate_float(value): 
     122    data = smart_str(value) 
     123    try: 
     124        float(data) 
     125    except ValueError: 
     126        raise ValidationError, _("Please enter a valid floating point number.") 
  • a/django/db/models/__init__.py

    old new  
    11from django.conf import settings 
    22from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured 
    3 from django.core import validators 
    43from django.db import connection 
    54from django.db.models.loading import get_apps, get_app, get_models, get_model, register_models 
    65from django.db.models.query import Q 
  • a/django/db/models/base.py

    old new  
    77import django.db.models.manipulators    # Imported to register signal handler. 
    88import django.db.models.manager         # Ditto. 
    99from django.core import validators 
     10from django.core.validation import ValidationError, NON_FIELD_ERRORS 
    1011from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError 
    1112from django.db.models.fields import AutoField, ImageField, FieldDoesNotExist 
    1213from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField 
     
    1920from django.utils.datastructures import SortedDict 
    2021from django.utils.functional import curry 
    2122from django.utils.encoding import smart_str, force_unicode, smart_unicode 
     23from django.utils.translation import ugettext as _ 
    2224from django.conf import settings 
    2325 
    2426try: 
     
    346348 
    347349    save_base.alters_data = True 
    348350 
    349     def validate(self): 
     351    def clean(self, new_data=None): 
     352        self.to_python() 
     353        self.validate(new_data) 
     354 
     355    def to_python(self): 
     356        error_dict = {} 
     357        for f in self._meta.fields: 
     358            try: 
     359                value = f.to_python(getattr(self, f.attname, f.get_default())) 
     360                setattr(self, f.attname, value) 
     361            except ValidationError, e: 
     362                error_dict[f.name] = e.messages 
     363        if error_dict: 
     364            raise ValidationError(error_dict) 
     365 
     366    def validate(self, new_data=None): 
    350367        """ 
    351368        First coerces all fields on this instance to their proper Python types. 
    352369        Then runs validation on every field. Returns a dictionary of 
    353370        field_name -> error_list. 
    354371        """ 
     372        if new_data is not None: 
     373            def get_value(f): 
     374                if f.name in new_data: 
     375                    return f.to_python(new_data[f.name]) 
     376                return getattr(self, f.attname, f.get_default()) 
     377        else: 
     378            get_value = lambda f: getattr(self, f.attname, f.get_default()) 
    355379        error_dict = {} 
    356         invalid_python = {} 
    357380        for f in self._meta.fields: 
    358381            try: 
    359                 setattr(self, f.attname, f.to_python(getattr(self, f.attname, f.get_default()))) 
    360             except validators.ValidationError, e: 
     382                value = get_value(f) 
     383                f.validate(value, instance=self) 
     384                if hasattr(self, 'validate_%s' % f.name): 
     385                    getattr(self, 'validate_%s' % f.name)(value) 
     386            except ValidationError, e: 
    361387                error_dict[f.name] = e.messages 
    362                 invalid_python[f.name] = 1 
    363         for f in self._meta.fields: 
    364             if f.name in invalid_python: 
    365                 continue 
    366             errors = f.validate_full(getattr(self, f.attname, f.get_default()), self.__dict__) 
    367             if errors: 
    368                 error_dict[f.name] = errors 
    369         return error_dict 
     388 
     389        for un_together in self._meta.unique_together: 
     390            lookup = {} 
     391            for name in un_together: 
     392                if name in error_dict: 
     393                    break 
     394                f = self._meta.get_field(name) 
     395                lookup['%s__exact' % name] = get_value(f) 
     396            try: 
     397                qset = self.__class__._default_manager.all() 
     398                if self.pk: 
     399                    qset = qset.exclude(pk=self.pk) 
     400                obj = qset.get(**lookup) 
     401                error_dict[NON_FIELD_ERRORS] = _('Fields %s must be unique.') % ', '.join(un_together) 
     402            except self.DoesNotExist: 
     403                pass 
     404 
     405        if error_dict: 
     406            raise ValidationError(error_dict) 
    370407 
    371408    def _collect_sub_objects(self, seen_objs): 
    372409        """ 
  • a/django/db/models/fields/__init__.py

    old new  
    1212from django.db.models.query_utils import QueryWrapper 
    1313from django.dispatch import dispatcher 
    1414from django.conf import settings 
     15from django.oldforms import validators as oldvalidators 
    1516from django.core import validators 
    1617from django import oldforms 
    1718from django import newforms as forms 
     
    8384    creation_counter = 0 
    8485    auto_creation_counter = -1 
    8586 
     87    validators = [] 
     88 
    8689    def __init__(self, verbose_name=None, name=None, primary_key=False, 
    8790            max_length=None, unique=False, blank=False, null=False, 
    8891            db_index=False, core=False, rel=None, default=NOT_PROVIDED, 
    8992            editable=True, serialize=True, prepopulate_from=None, 
    9093            unique_for_date=None, unique_for_month=None, unique_for_year=None, 
    9194            validator_list=None, choices=None, radio_admin=None, help_text='', 
    92             db_column=None, db_tablespace=None, auto_created=False): 
     95            db_column=None, db_tablespace=None, auto_created=False, validators=[]): 
    9396        self.name = name 
    9497        self.verbose_name = verbose_name 
    9598        self.primary_key = primary_key 
     
    102105        self.core, self.rel, self.default = core, rel, default 
    103106        self.editable = editable 
    104107        self.serialize = serialize 
     108        self.validators = validators + self.validators 
    105109        self.validator_list = validator_list or [] 
    106110        self.prepopulate_from = prepopulate_from 
    107111        self.unique_for_date, self.unique_for_month = unique_for_date, unique_for_month 
     
    169173        except KeyError: 
    170174            return None 
    171175 
    172     def validate_full(self, field_data, all_data): 
    173         """ 
    174         Returns a list of errors for this field. This is the main interface, 
    175         as it encapsulates some basic validation logic used by all fields. 
    176         Subclasses should implement validate(), not validate_full(). 
    177         """ 
    178         if not self.blank and not field_data: 
    179             return [_('This field is required.')] 
    180         try: 
    181             self.validate(field_data, all_data) 
    182         except validators.ValidationError, e: 
    183             return e.messages 
    184         return [] 
    185  
    186     def validate(self, field_data, all_data): 
     176    def validate(self, value, instance=None): 
    187177        """ 
    188         Raises validators.ValidationError if field_data has any errors. 
     178        Raises validators.ValidationError if value has any errors. 
    189179        Subclasses should override this to specify field-specific validation 
    190         logic. This method should assume field_data has already been converted 
     180        logic. This method should assume value has already been converted 
    191181        into the appropriate data type by Field.to_python(). 
    192182        """ 
    193         pass 
     183        if not self.blank and self.editable and not value: 
     184            raise validators.ValidationError(_('This field is required.')) 
     185        elist = [] 
     186        for validator in self.validators: 
     187            validator(value)     
     188        if self.unique and instance: 
     189            try: 
     190                qset = instance.__class__._default_manager.all() 
     191                if instance.pk: 
     192                    qset = qset.exclude(pk=instance.pk) 
     193                obj = qset.get(**{'%s__exact' % self.name : value}) 
     194                raise validators.ValidationError(_('This field must be unique')) 
     195            except instance.DoesNotExist: 
     196                pass 
    194197 
    195198    def set_attributes_from_name(self, name): 
    196199        self.name = name 
     
    345348                    core_field_names.extend(f.get_manipulator_field_names(name_prefix)) 
    346349            # Now, if there are any, add the validator to this FormField. 
    347350            if core_field_names: 
    348                 params['validator_list'].append(validators.RequiredIfOtherFieldsGiven(core_field_names, ugettext_lazy("This field is required."))) 
     351                params['validator_list'].append(oldvalidators.RequiredIfOtherFieldsGiven(core_field_names, ugettext_lazy("This field is required."))) 
    349352 
    350353        # Finally, add the field_names. 
    351354        field_names = self.get_manipulator_field_names(name_prefix) 
     
    541544            return value.date() 
    542545        if isinstance(value, datetime.date): 
    543546            return value 
    544         validators.isValidANSIDate(value, None) 
    545547        try: 
    546548            return datetime.date(*time.strptime(value, '%Y-%m-%d')[:3]) 
    547549        except ValueError: 
     
    734736        return super(DecimalField, self).formfield(**defaults) 
    735737 
    736738class EmailField(CharField): 
     739    validators = [validators.validate_email] 
    737740    def __init__(self, *args, **kwargs): 
    738741        kwargs['max_length'] = kwargs.get('max_length', 75) 
    739742        CharField.__init__(self, *args, **kwargs) 
     
    741744    def get_manipulator_field_objs(self): 
    742745        return [oldforms.EmailField] 
    743746 
    744     def validate(self, field_data, all_data): 
    745         validators.isValidEmail(field_data, all_data) 
    746  
    747747    def formfield(self, **kwargs): 
    748748        defaults = {'form_class': forms.EmailField} 
    749749        defaults.update(kwargs) 
     
    777777                        self.always_test = True 
    778778                    def __call__(self, field_data, all_data): 
    779779                        if not all_data.get(self.other_file_field_name, False): 
    780                             c = validators.RequiredIfOtherFieldsGiven(self.other_field_names, ugettext_lazy("This field is required.")) 
     780                            c = oldvalidators.RequiredIfOtherFieldsGiven(self.other_field_names, ugettext_lazy("This field is required.")) 
    781781                            c(field_data, all_data) 
    782782                # First, get the core fields, if any. 
    783783                core_field_names = [] 
     
    788788                if core_field_names: 
    789789                    field_list[0].validator_list.append(RequiredFileField(core_field_names, field_list[1].field_name)) 
    790790            else: 
    791                 v = validators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, ugettext_lazy("This field is required.")) 
     791                v = oldvalidators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, ugettext_lazy("This field is required.")) 
    792792                v.always_test = True 
    793793                field_list[0].validator_list.append(v) 
    794794                field_list[0].is_required = field_list[1].is_required = False 
     
    946946 
    947947class IPAddressField(Field): 
    948948    empty_strings_allowed = False 
     949    validators = [validators.validate_ip_address4] 
     950 
    949951    def __init__(self, *args, **kwargs): 
    950952        kwargs['max_length'] = 15 
    951953        Field.__init__(self, *args, **kwargs) 
     
    956958    def get_internal_type(self): 
    957959        return "IPAddressField" 
    958960 
    959     def validate(self, field_data, all_data): 
    960         validators.isValidIPAddress4(field_data, None) 
    961  
    962961    def formfield(self, **kwargs): 
    963962        defaults = {'form_class': forms.IPAddressField} 
    964963        defaults.update(kwargs) 
     
    989988        return super(NullBooleanField, self).formfield(**defaults) 
    990989 
    991990class PhoneNumberField(IntegerField): 
     991    validators = [validators.validate_phone_number] 
     992 
    992993    def get_manipulator_field_objs(self): 
    993994        return [oldforms.PhoneNumberField] 
    994995 
    995996    def get_internal_type(self): 
    996997        return "PhoneNumberField" 
    997998 
    998     def validate(self, field_data, all_data): 
    999         validators.isValidPhone(field_data, all_data) 
    1000  
    1001999    def formfield(self, **kwargs): 
    10021000        from django.contrib.localflavor.us.forms import USPhoneNumberField 
    10031001        defaults = {'form_class': USPhoneNumberField} 
     
    10291027        return super(PositiveSmallIntegerField, self).formfield(**defaults) 
    10301028 
    10311029class SlugField(CharField): 
     1030    validators = [validators.validate_slug] 
    10321031    def __init__(self, *args, **kwargs): 
    10331032        kwargs['max_length'] = kwargs.get('max_length', 50) 
    1034         kwargs.setdefault('validator_list', []).append(validators.isSlug) 
    10351033        # Set db_index=True unless it's been set manually. 
    10361034        if 'db_index' not in kwargs: 
    10371035            kwargs['db_index'] = True 
     
    11271125    def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs): 
    11281126        kwargs['max_length'] = kwargs.get('max_length', 200) 
    11291127        if verify_exists: 
    1130             kwargs.setdefault('validator_list', []).append(validators.isExistingURL
     1128            kwargs.setdefault('validators', []).append(validators.validate_existing_url
    11311129        self.verify_exists = verify_exists 
    11321130        CharField.__init__(self, verbose_name, name, **kwargs) 
    11331131 
  • a/django/db/models/fields/related.py

    old new  
    792792        objects = mod._default_manager.in_bulk(pks) 
    793793        if len(objects) != len(pks): 
    794794            badkeys = [k for k in pks if k not in objects] 
    795             raise validators.ValidationError, ungettext("Please enter valid %(self)s IDs. The value %(value)r is invalid.", 
     795            raise validator_list.ValidationError, ungettext("Please enter valid %(self)s IDs. The value %(value)r is invalid.", 
    796796                    "Please enter valid %(self)s IDs. The values %(value)r are invalid.", len(badkeys)) % { 
    797797                'self': self.verbose_name, 
    798798                'value': len(badkeys) == 1 and badkeys[0] or tuple(badkeys), 
  • a/django/newforms/__init__.py

    old new  
    1010    "This form field requires foo.js" and form.js_includes() 
    1111""" 
    1212 
    13 from util import ValidationError 
     13from django.core.validation import ValidationError 
    1414from widgets import * 
    1515from fields import * 
    1616from forms import * 
  • a/django/newforms/fields.py

    old new  
    1919 
    2020from django.utils.translation import ugettext_lazy as _ 
    2121from django.utils.encoding import StrAndUnicode, smart_unicode, smart_str 
     22from django.core.validation import ValidationError, ErrorList, TypeCoercionError 
    2223 
    23 from util import ErrorList, ValidationError 
    2424from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput 
    2525 
    2626 
     
    4141 
    4242class Field(object): 
    4343    widget = TextInput # Default widget to use when rendering this type of Field. 
     44    validators = [] 
    4445    hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden". 
    4546    default_error_messages = { 
    4647        'required': _(u'This field is required.'), 
     
    5152    creation_counter = 0 
    5253 
    5354    def __init__(self, required=True, widget=None, label=None, initial=None, 
    54                  help_text=None, error_messages=None): 
     55                 help_text=None, error_messages=None, validators=[]): 
    5556        # required -- Boolean that specifies whether the field is required. 
    5657        #             True by default. 
    5758        # widget -- A Widget class, or instance of a Widget class, that should 
     
    6566        # initial -- A value to use in this Field's initial display. This value 
    6667        #            is *not* used as a fallback if data isn't given. 
    6768        # help_text -- An optional string to use as "help text" for this Field. 
     69        # validators -- Optional list of additional validator functions 
    6870        if label is not None: 
    6971            label = smart_unicode(label) 
     72        self.validators = self.validators + validators 
    7073        self.required, self.label, self.initial = required, label, initial 
    7174        self.help_text = smart_unicode(help_text or '') 
    7275        widget = widget or self.widget 
     
    9497        messages.update(error_messages or {}) 
    9598        self.error_messages = messages 
    9699 
     100    def to_python(self, value): 
     101        if value in EMPTY_VALUES: 
     102            return None 
     103        return smart_unicode(value) 
     104 
     105    def validate(self, value): 
     106        if self.required and value in EMPTY_VALUES: 
     107            raise ValidationError(self.error_messages['required']) 
     108        elist = ErrorList() 
     109        for validator in self.validators: 
     110            try: 
     111                validator(value) 
     112            except ValidationError, e: 
     113                elist.extend(e.messages) 
     114        if elist: 
     115            raise ValidationError(elist) 
     116 
    97117    def clean(self, value): 
    98118        """ 
    99119        Validates the given value and returns its "cleaned" value as an 
     
    101121 
    102122        Raises ValidationError for any errors. 
    103123        """ 
    104         if self.required and value in EMPTY_VALUES: 
    105             raise ValidationError(self.error_messages['required']
     124        value = self.to_python(value) 
     125        self.validate(value
    106126        return value 
    107127 
    108128    def widget_attrs(self, widget): 
     
    129149        self.max_length, self.min_length = max_length, min_length 
    130150        super(CharField, self).__init__(*args, **kwargs) 
    131151 
    132     def clean(self, value): 
    133         "Validates max_length and min_length. Returns a Unicode object." 
    134         super(CharField, self).clean(value) 
     152    def to_python(self, value): 
    135153        if value in EMPTY_VALUES: 
    136154            return u'' 
    137         value = smart_unicode(value) 
     155        return smart_unicode(value) 
     156 
     157    def validate(self, value): 
     158        "Validates max_length and min_length. Returns a Unicode object." 
     159        super(CharField, self).validate(value) 
    138160        value_length = len(value) 
     161        if value_length == 0 and not self.required: 
     162            return  
    139163        if self.max_length is not None and value_length > self.max_length: 
    140164            raise ValidationError(self.error_messages['max_length'] % {'max': self.max_length, 'length': value_length}) 
    141165        if self.min_length is not None and value_length < self.min_length: 
    142166            raise ValidationError(self.error_messages['min_length'] % {'min': self.min_length, 'length': value_length}) 
    143         return value 
    144167 
    145168    def widget_attrs(self, widget): 
    146169        if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)): 
     
    158181        self.max_value, self.min_value = max_value, min_value 
    159182        super(IntegerField, self).__init__(*args, **kwargs) 
    160183 
    161     def clean(self, value): 
    162         """ 
    163         Validates that int() can be called on the input. Returns the result 
    164         of int(). Returns None for empty values. 
    165         """ 
    166         super(IntegerField, self).clean(value) 
     184    def to_python(self, value): 
    167185        if value in EMPTY_VALUES: 
    168186            return None 
    169187        try: 
    170             value = int(str(value)) 
     188            return int(smart_str(value)) 
    171189        except (ValueError, TypeError): 
    172             raise ValidationError(self.error_messages['invalid']) 
     190            raise TypeCoercionError(self.error_messages['invalid']) 
     191 
     192    def validate(self, value): 
     193        """ 
     194        Validates that int() can be called on the input. Returns the result 
     195        of int(). Returns None for empty values. 
     196        """ 
     197        super(IntegerField, self).validate(value) 
     198        if value is None: return 
    173199        if self.max_value is not None and value > self.max_value: 
    174200            raise ValidationError(self.error_messages['max_value'] % self.max_value) 
    175201        if self.min_value is not None and value < self.min_value: 
    176202            raise ValidationError(self.error_messages['min_value'] % self.min_value) 
    177         return value 
    178203 
    179204class FloatField(Field): 
    180205    default_error_messages = { 
     
    187212        self.max_value, self.min_value = max_value, min_value 
    188213        Field.__init__(self, *args, **kwargs) 
    189214 
    190     def clean(self, value): 
     215    def to_python(self, value): 
    191216        """ 
    192217        Validates that float() can be called on the input. Returns a float. 
    193218        Returns None for empty values. 
    194219        """ 
    195         super(FloatField, self).clean(value) 
    196         if not self.required and value in EMPTY_VALUES: 
     220        if value in EMPTY_VALUES: 
    197221            return None 
    198222        try: 
    199             value = float(value) 
     223            return float(value) 
    200224        except (ValueError, TypeError): 
    201             raise ValidationError(self.error_messages['invalid']) 
     225            raise TypeCoercionError(self.error_messages['invalid']) 
     226 
     227    def validate(self, value): 
     228        super(FloatField, self).validate(value) 
     229        if value is None: return 
    202230        if self.max_value is not None and value > self.max_value: 
    203231            raise ValidationError(self.error_messages['max_value'] % self.max_value) 
    204232        if self.min_value is not None and value < self.min_value: 
    205233            raise ValidationError(self.error_messages['min_value'] % self.min_value) 
    206         return value 
    207234 
    208235class DecimalField(Field): 
    209236    default_error_messages = { 
     
    220247        self.max_digits, self.decimal_places = max_digits, decimal_places 
    221248        Field.__init__(self, *args, **kwargs) 
    222249 
    223     def clean(self, value): 
     250    def to_python(self, value): 
    224251        """ 
    225252        Validates that the input is a decimal number. Returns a Decimal 
    226253        instance. Returns None for empty values. Ensures that there are no more 
    227254        than max_digits in the number, and no more than decimal_places digits 
    228255        after the decimal point. 
    229256        """ 
    230         super(DecimalField, self).clean(value) 
    231         if not self.required and value in EMPTY_VALUES: 
     257        if value in EMPTY_VALUES: 
    232258            return None 
    233259        value = smart_str(value).strip() 
    234260        try: 
    235             value = Decimal(value) 
     261            return Decimal(value) 
    236262        except DecimalException: 
    237             raise ValidationError(self.error_messages['invalid']) 
     263            raise TypeCoercionError(self.error_messages['invalid']) 
     264 
     265    def validate(self, value): 
     266        super(DecimalField, self).validate(value) 
     267        if value is None: return 
    238268        pieces = str(value).lstrip("-").split('.') 
    239269        decimals = (len(pieces) == 2) and len(pieces[1]) or 0 
    240270        digits = len(pieces[0]) 
     
    248278            raise ValidationError(self.error_messages['max_decimal_places'] % self.decimal_places) 
    249279        if self.max_digits is not None and self.decimal_places is not None and digits > (self.max_digits - self.decimal_places): 
    250280            raise ValidationError(self.error_messages['max_whole_digits'] % (self.max_digits - self.decimal_places)) 
    251         return value 
    252281 
    253282DEFAULT_DATE_INPUT_FORMATS = ( 
    254283    '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06' 
     
    267296        super(DateField, self).__init__(*args, **kwargs) 
    268297        self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS 
    269298 
    270     def clean(self, value): 
     299    def to_python(self, value): 
    271300        """ 
    272301        Validates that the input can be converted to a date. Returns a Python 
    273302        datetime.date object. 
    274303        """ 
    275         super(DateField, self).clean(value) 
    276304        if value in EMPTY_VALUES: 
    277305            return None 
    278306        if isinstance(value, datetime.datetime): 
     
    284312                return datetime.date(*time.strptime(value, format)[:3]) 
    285313            except ValueError: 
    286314                continue 
    287         raise ValidationError(self.error_messages['invalid']) 
     315        raise TypeCoercionError(self.error_messages['invalid']) 
    288316 
    289317DEFAULT_TIME_INPUT_FORMATS = ( 
    290318    '%H:%M:%S',     # '14:30:59' 
     
    300328        super(TimeField, self).__init__(*args, **kwargs) 
    301329        self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS 
    302330 
    303     def clean(self, value): 
     331    def to_python(self, value): 
    304332        """ 
    305333        Validates that the input can be converted to a time. Returns a Python 
    306334        datetime.time object. 
    307335        """ 
    308         super(TimeField, self).clean(value) 
    309336        if value in EMPTY_VALUES: 
    310337            return None 
    311338        if isinstance(value, datetime.time): 
     
    315342                return datetime.time(*time.strptime(value, format)[3:6]) 
    316343            except ValueError: 
    317344                continue 
    318         raise ValidationError(self.error_messages['invalid']) 
     345        raise TypeCoercionError(self.error_messages['invalid']) 
    319346 
    320347DEFAULT_DATETIME_INPUT_FORMATS = ( 
    321348    '%Y-%m-%d %H:%M:%S',     # '2006-10-25 14:30:59' 
     
    339366        super(DateTimeField, self).__init__(*args, **kwargs) 
    340367        self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS 
    341368 
    342     def clean(self, value): 
     369    def to_python(self, value): 
    343370        """ 
    344371        Validates that the input can be converted to a datetime. Returns a 
    345372        Python datetime.datetime object. 
    346373        """ 
    347         super(DateTimeField, self).clean(value) 
    348374        if value in EMPTY_VALUES: 
    349375            return None 
    350376        if isinstance(value, datetime.datetime): 
     
    362388                return datetime.datetime(*time.strptime(value, format)[:6]) 
    363389            except ValueError: 
    364390                continue 
    365         raise ValidationError(self.error_messages['invalid']) 
     391        raise TypeCoercionError(self.error_messages['invalid']) 
    366392 
    367393class RegexField(CharField): 
    368394    def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs): 
     
    381407            regex = re.compile(regex) 
    382408        self.regex = regex 
    383409 
    384     def clean(self, value): 
     410    def validate(self, value): 
    385411        """ 
    386412        Validates that the input matches the regular expression. Returns a 
    387413        Unicode object. 
    388414        """ 
    389         value = super(RegexField, self).clean(value) 
    390         if value == u''
    391             return value 
     415        super(RegexField, self).validate(value) 
     416        if value in EMPTY_VALUES
     417            return u'' 
    392418        if not self.regex.search(value): 
    393419            raise ValidationError(self.error_messages['invalid']) 
    394         return value 
    395420 
    396421email_re = re.compile( 
    397422    r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*"  # dot-atom 
     
    431456    widget = FileInput 
    432457    default_error_messages = { 
    433458        'invalid': _(u"No file was submitted. Check the encoding type on the form."), 
    434         'missing': _(u"No file was submitted."), 
    435459        'empty': _(u"The submitted file is empty."), 
    436460    } 
    437461 
    438     def __init__(self, *args, **kwargs): 
    439         super(FileField, self).__init__(*args, **kwargs) 
    440  
    441     def clean(self, data, initial=None): 
    442         super(FileField, self).clean(initial or data) 
     462    def to_python(self, data, initial=None): 
    443463        if not self.required and data in EMPTY_VALUES: 
    444464            return None 
    445465        elif not data and initial: 
    446466            return initial 
     467        elif not data: 
     468            return None 
     469 
    447470        try: 
    448471            f = UploadedFile(data['filename'], data['content']) 
    449472        except TypeError: 
    450             raise ValidationError(self.error_messages['invalid']) 
     473            raise TypeCoercionError(self.error_messages['invalid']) 
    451474        except KeyError: 
    452             raise ValidationError(self.error_messages['missing']) 
     475            raise ValidationError(self.error_messages['required']) 
    453476        if not f.content: 
    454477            raise ValidationError(self.error_messages['empty']) 
    455478        return f 
    456479 
     480    def clean(self, data, initial=None): 
     481        "overriden clean to provide extra argument initial" 
     482        value = self.to_python(data, initial) 
     483        self.validate(value) 
     484        return value 
     485 
    457486class ImageField(FileField): 
    458487    default_error_messages = { 
    459488        'invalid_image': _(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image."), 
    460489    } 
    461490 
    462     def clean(self, data, initial=None): 
     491    def validate(self, value): 
    463492        """ 
    464493        Checks that the file-upload field data contains a valid image (GIF, JPG, 
    465494        PNG, possibly others -- whatever the Python Imaging Library supports). 
    466495        """ 
    467         f = super(ImageField, self).clean(data, initial) 
    468         if f is None: 
    469             return None 
    470         elif not data and initial: 
    471             return initial 
     496        super(ImageField, self).validate(value) 
     497 
     498        if value is None: 
     499            return 
    472500        from PIL import Image 
    473501        from cStringIO import StringIO 
    474502        try: 
    475503            # load() is the only method that can spot a truncated JPEG, 
    476504            #  but it cannot be called sanely after verify() 
    477             trial_image = Image.open(StringIO(f.content)) 
     505            trial_image = Image.open(StringIO(value.content)) 
    478506            trial_image.load() 
    479507            # verify() is the only method that can spot a corrupt PNG, 
    480508            #  but it must be called immediately after the constructor 
    481             trial_image = Image.open(StringIO(f.content)) 
     509            trial_image = Image.open(StringIO(value.content)) 
    482510            trial_image.verify() 
    483511        except Exception: # Python Imaging Library doesn't recognize it as an image 
    484512            raise ValidationError(self.error_messages['invalid_image']) 
    485         return f 
    486513 
    487514url_re = re.compile( 
    488515    r'^https?://' # http:// or https:// 
     
    505532        self.verify_exists = verify_exists 
    506533        self.user_agent = validator_user_agent 
    507534 
    508     def clean(self, value): 
    509         # If no URL scheme given, assume http:// 
     535    def to_python(self, value): 
     536        value = super(URLField, self).to_python(value) 
    510537        if value and '://' not in value: 
    511538            value = u'http://%s' % value 
    512         value = super(URLField, self).clean(value) 
    513         if value == u'': 
    514             return value 
     539        return value 
     540 
     541    def validate(self, value): 
     542        # If no URL scheme given, assume http:// 
     543        super(URLField, self).validate(value) 
     544        if value in EMPTY_VALUES: 
     545            return 
    515546        if self.verify_exists: 
    516547            import urllib2 
    517548            from django.conf import settings 
     
    529560                raise ValidationError(self.error_messages['invalid']) 
    530561            except: # urllib2.URLError, httplib.InvalidURL, etc. 
    531562                raise ValidationError(self.error_messages['invalid_link']) 
    532         return value 
    533563 
    534564class BooleanField(Field): 
    535565    widget = CheckboxInput 
    536566 
    537     def clean(self, value): 
     567    def to_python(self, value): 
    538568        """Returns a Python boolean object.""" 
    539         super(BooleanField, self).clean(value) 
     569        if self.required and value in EMPTY_VALUES: 
     570            raise ValidationError(self.error_messages['required']) 
    540571        # Explicitly check for the string 'False', which is what a hidden field 
    541572        # will submit for False. Because bool("True") == True, we don't need to 
    542573        # handle that explicitly. 
     
    544575            return False 
    545576        return bool(value) 
    546577 
     578 
    547579class NullBooleanField(BooleanField): 
    548580    """ 
    549581    A field whose valid values are None, True and False. Invalid values are 
    550582    cleaned to None. 
     583 
     584    Note that validation doesn't apply here. 
    551585    """ 
    552586    widget = NullBooleanSelect 
    553587 
    554     def clean(self, value): 
     588    def to_python(self, value): 
    555589        return {True: True, False: False}.get(value, None) 
    556590 
     591    def validate(self, value): 
     592        pass 
     593 
     594 
    557595class ChoiceField(Field): 
    558596    widget = Select 
    559597    default_error_messages = { 
     
    577615 
    578616    choices = property(_get_choices, _set_choices) 
    579617 
    580     def clean(self, value): 
    581         """ 
    582         Validates that the input is in self.choices. 
    583         """ 
    584         value = super(ChoiceField, self).clean(value) 
    585         if value in EMPTY_VALUES: 
    586             value = u'' 
    587         value = smart_unicode(value) 
    588         if value == u'': 
    589             return value 
     618    def validate(self, value): 
     619        super(ChoiceField, self).validate(value) 
     620        if value in EMPTY_VALUES and not self.required: 
     621            return u'' 
    590622        valid_values = set([smart_unicode(k) for k, v in self.choices]) 
    591623        if value not in valid_values: 
    592624            raise ValidationError(self.error_messages['invalid_choice'] % {'value': value}) 
    593         return value 
    594625 
    595626class MultipleChoiceField(ChoiceField): 
    596627    hidden_widget = MultipleHiddenInput 
     
    600631        'invalid_list': _(u'Enter a list of values.'), 
    601632    } 
    602633 
    603     def clean(self, value): 
     634    def to_python(self, value): 
    604635        """ 
    605636        Validates that the input is a list or tuple. 
    606637        """ 
    607         if self.required and not value: 
    608             raise ValidationError(self.error_messages['required']) 
    609         elif not self.required and not value: 
     638        if not value: 
    610639            return [] 
    611640        if not isinstance(value, (list, tuple)): 
    612             raise ValidationError(self.error_messages['invalid_list']) 
    613         new_value = [smart_unicode(val) for val in value] 
     641            raise TypeCoercionError(self.error_messages['invalid_list']) 
     642        return [smart_unicode(val) for val in value] 
     643 
     644    def validate(self, value): 
    614645        # Validate that each value in the value list is in self.choices. 
     646        if self.required and value == []: 
     647            raise ValidationError(self.error_messages['required']) 
    615648        valid_values = set([smart_unicode(k) for k, v in self.choices]) 
    616         for val in new_value: 
     649        for val in value: 
    617650            if val not in valid_values: 
    618651                raise ValidationError(self.error_messages['invalid_choice'] % {'value': val}) 
    619         return new_value 
    620652 
    621653class ComboField(Field): 
    622654    """ 
     
    631663            f.required = False 
    632664        self.fields = fields 
    633665 
    634     def clean(self, value): 
     666    def to_python(self, value): 
     667        for field in self.fields: 
     668            value = field.to_python(value) 
     669        return value 
     670 
     671    def validate(self, value): 
    635672        """ 
    636673        Validates the given value against all of self.fields, which is a 
    637674        list of Field instances. 
    638675        """ 
    639         super(ComboField, self).clean(value) 
     676        super(ComboField, self).validate(value) 
    640677        for field in self.fields: 
    641             value = field.clean(value) 
    642         return value 
     678            field.validate(value) 
    643679 
    644680class MultiValueField(Field): 
    645681    """ 
     
    671707            f.required = False 
    672708        self.fields = fields 
    673709 
    674     def clean(self, value): 
     710    def to_python(self, value): 
    675711        """ 
    676712        Validates every value in the given list. A value is validated against 
    677713        the corresponding Field in self.fields. 
     
    685721        if not value or isinstance(value, (list, tuple)): 
    686722            if not value or not [v for v in value if v not in EMPTY_VALUES]: 
    687723                if self.required: 
    688                     raise ValidationError(self.error_messages['required']) 
     724                    return None 
    689725                else: 
    690726                    return self.compress([]) 
    691727        else: 
    692             raise ValidationError(self.error_messages['invalid']) 
     728            raise TypeCoercionError(self.error_messages['invalid']) 
     729 
    693730        for i, field in enumerate(self.fields): 
    694731            try: 
    695732                field_value = value[i] 
  • a/django/newforms/forms.py

    old new  
    88from django.utils.html import escape 
    99from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode 
    1010from django.utils.safestring import mark_safe 
     11from django.core.validation import ValidationError, ErrorList, NON_FIELD_ERRORS 
    1112 
    1213from fields import Field, FileField 
    1314from widgets import TextInput, Textarea 
    14 from util import flatatt, ErrorDict, ErrorList, ValidationError 
     15from util import flatatt, ErrorDict 
    1516 
    1617__all__ = ('BaseForm', 'Form') 
    1718 
    18 NON_FIELD_ERRORS = '__all__' 
    1919 
    2020def pretty_name(name): 
    2121    "Converts 'first_name' to 'First name'" 
     
    205205                else: 
    206206                    value = field.clean(value) 
    207207                self.cleaned_data[name] = value 
     208                # FIXME deprecated - keeping this here for backwards compatibility 
    208209                if hasattr(self, 'clean_%s' % name): 
    209210                    value = getattr(self, 'clean_%s' % name)() 
    210211                    self.cleaned_data[name] = value 
     212 
     213                if hasattr(self, 'validate_%s' % name): 
     214                    getattr(self, 'validate_%s' % name)(value) 
    211215            except ValidationError, e: 
    212216                self._errors[name] = e.messages 
    213217                if name in self.cleaned_data: 
    214218                    del self.cleaned_data[name] 
    215219        try: 
    216             self.cleaned_data = self.clean() 
     220            self.validate() 
    217221        except ValidationError, e: 
    218             self._errors[NON_FIELD_ERRORS] = e.messages 
     222            if hasattr(e, 'message_dict'): 
     223                for k, v in e.message_dict.items(): 
     224                    self._errors.setdefault(k, []).extend(v) 
     225            else: 
     226                self._errors[NON_FIELD_ERRORS] = e.messages 
    219227        if self._errors: 
    220228            delattr(self, 'cleaned_data') 
    221229 
    222230    def clean(self): 
    223231        """ 
     232        FIXME: deprecated, use validate() instead 
     233 
    224234        Hook for doing any extra form-wide cleaning after Field.clean() been 
    225235        called on every field. Any ValidationError raised by this method will 
    226236        not be associated with a particular field; it will have a special-case 
     
    228238        """ 
    229239        return self.cleaned_data 
    230240 
     241    def validate(self): 
     242        self.cleaned_data = self.clean() 
     243 
    231244    def is_multipart(self): 
    232245        """ 
    233246        Returns True if the form needs to be multipart-encrypted, i.e. it has 
  • a/django/newforms/models.py

    old new  
    99from django.utils.encoding import smart_unicode 
    1010from django.utils.datastructures import SortedDict 
    1111from django.core.exceptions import ImproperlyConfigured 
     12from django.core.validation import ValidationError, ErrorList, TypeCoercionError 
    1213 
    13 from util import ValidationError, ErrorList 
    1414from forms import BaseForm, get_declared_fields 
    1515from fields import Field, ChoiceField, EMPTY_VALUES 
    1616from widgets import Select, SelectMultiple, MultipleHiddenInput 
     
    257257            object_data.update(initial) 
    258258        BaseForm.__init__(self, data, files, auto_id, prefix, object_data, error_class, label_suffix) 
    259259 
     260    def validate(self): 
     261        super(BaseModelForm, self).validate() 
     262        if self._errors: 
     263            return 
     264        self.instance.clean(self.cleaned_data) 
     265 
    260266    def save(self, commit=True): 
    261267        """ 
    262268        Saves this ``form``'s cleaned_data into model instance 
     
    354360 
    355361    choices = property(_get_choices, _set_choices) 
    356362 
    357     def clean(self, value): 
    358         Field.clean(self, value) 
    359         if value in EMPTY_VALUES: 
     363    def to_python(self, value): 
     364        if self.required and value in EMPTY_VALUES: 
     365            raise ValidationError(self.error_messages['required']) 
     366        elif value in EMPTY_VALUES: 
    360367            return None 
    361368        try: 
    362369            value = self.queryset.get(pk=value) 
     
    364371            raise ValidationError(self.error_messages['invalid_choice']) 
    365372        return value 
    366373 
     374    def validate(self, value): 
     375        pass 
     376 
    367377class ModelMultipleChoiceField(ModelChoiceField): 
    368378    """A MultipleChoiceField whose choices are a model QuerySet.""" 
    369379    hidden_widget = MultipleHiddenInput 
     
    380390            cache_choices, required, widget, label, initial, help_text, 
    381391            *args, **kwargs) 
    382392 
    383     def clean(self, value): 
     393    def to_python(self, value): 
    384394        if self.required and not value: 
    385395            raise ValidationError(self.error_messages['required']) 
    386396        elif not self.required and not value: 
    387397            return [] 
    388398        if not isinstance(value, (list, tuple)): 
    389             raise ValidationError(self.error_messages['list']) 
     399            raise TypeCoercionError(self.error_messages['list']) 
    390400        final_values = [] 
    391401        for val in value: 
    392402            try: 
     
    396406            else: 
    397407                final_values.append(obj) 
    398408        return final_values 
     409 
  • a/django/newforms/util.py

    old new  
    3030    def as_text(self): 
    3131        return u'\n'.join([u'* %s\n%s' % (k, u'\n'.join([u'  * %s' % force_unicode(i) for i in v])) for k, v in self.items()]) 
    3232 
    33 class ErrorList(list, StrAndUnicode): 
    34     """ 
    35     A collection of errors that knows how to display itself in various formats. 
    36     """ 
    37     def __unicode__(self): 
    38         return self.as_ul() 
    39  
    40     def as_ul(self): 
    41         if not self: return u'' 
    42         return mark_safe(u'<ul class="errorlist">%s</ul>' 
    43                 % ''.join([u'<li>%s</li>' % force_unicode(e) for e in self])) 
    44  
    45     def as_text(self): 
    46         if not self: return u'' 
    47         return u'\n'.join([u'* %s' % force_unicode(e) for e in self]) 
    48  
    49     def __repr__(self): 
    50         return repr([force_unicode(e) for e in self]) 
    51  
    52 class ValidationError(Exception): 
    53     def __init__(self, message): 
    54         """ 
    55         ValidationError can be passed any object that can be printed (usually 
    56         a string) or a list of objects. 
    57         """ 
    58         if isinstance(message, list): 
    59             self.messages = ErrorList([smart_unicode(msg) for msg in message]) 
    60         else: 
    61             message = smart_unicode(message) 
    62             self.messages = ErrorList([message]) 
    63  
    64     def __str__(self): 
    65         # This is needed because, without a __str__(), printing an exception 
    66         # instance would result in this: 
    67         # AttributeError: ValidationError instance has no attribute 'args' 
    68         # See http://www.python.org/doc/current/tut/node10.html#handling 
    69         return repr(self.messages) 
  • a/django/oldforms/__init__.py

    old new  
    1 from django.core import validators 
     1from django.oldforms import validators 
    22from django.core.exceptions import PermissionDenied 
    33from django.utils.html import escape 
    44from django.utils.safestring import mark_safe 
  • /dev/null

    old new  
     1""" 
     2A library of validators that return None and raise ValidationError when the 
     3provided data isn't valid. 
     4 
     5Validators may be callable classes, and they may have an 'always_test' 
     6attribute. If an 'always_test' attribute exists (regardless of value), the 
     7validator will *always* be run, regardless of whether its associated 
     8form field is required. 
     9""" 
     10 
     11import urllib2 
     12import re 
     13try: 
     14    from decimal import Decimal, DecimalException 
     15except ImportError: 
     16    from django.utils._decimal import Decimal, DecimalException    # Python 2.3 
     17 
     18from django.conf import settings 
     19from django.utils.translation import ugettext as _, ugettext_lazy, ungettext 
     20from django.utils.functional import Promise, lazy 
     21from django.utils.encoding import force_unicode, smart_str 
     22from django.core.validation import ValidationError 
     23 
     24_datere = r'\d{4}-\d{1,2}-\d{1,2}' 
     25_timere = r'(?:[01]?[0-9]|2[0-3]):[0-5][0-9](?::[0-5][0-9])?' 
     26alnum_re = re.compile(r'^\w+$') 
     27alnumurl_re = re.compile(r'^[-\w/]+$') 
     28ansi_date_re = re.compile('^%s$' % _datere) 
     29ansi_time_re = re.compile('^%s$' % _timere) 
     30ansi_datetime_re = re.compile('^%s %s$' % (_datere, _timere)) 
     31email_re = re.compile( 
     32    r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*"  # dot-atom 
     33    r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"' # quoted-string 
     34    r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE)  # domain 
     35integer_re = re.compile(r'^-?\d+$') 
     36ip4_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}$') 
     37phone_re = re.compile(r'^[A-PR-Y0-9]{3}-[A-PR-Y0-9]{3}-[A-PR-Y0-9]{4}$', re.IGNORECASE) 
     38slug_re = re.compile(r'^[-\w]+$') 
     39url_re = re.compile(r'^https?://\S+$') 
     40 
     41lazy_inter = lazy(lambda a,b: force_unicode(a) % b, unicode) 
     42 
     43 
     44class CriticalValidationError(Exception): 
     45    def __init__(self, message): 
     46        "ValidationError can be passed a string or a list." 
     47        if isinstance(message, list): 
     48            self.messages = [force_unicode(msg) for msg in message] 
     49        else: 
     50            assert isinstance(message, (basestring, Promise)), ("'%s' should be a string" % message) 
     51            self.messages = [force_unicode(message)] 
     52 
     53    def __str__(self): 
     54        return str(self.messages) 
     55 
     56 
     57def isAlphaNumeric(field_data, all_data): 
     58    if not alnum_re.search(field_data): 
     59        raise ValidationError, _("This value must contain only letters, numbers and underscores.") 
     60 
     61def isAlphaNumericURL(field_data, all_data): 
     62    if not alnumurl_re.search(field_data): 
     63        raise ValidationError, _("This value must contain only letters, numbers, underscores, dashes or slashes.") 
     64 
     65def isSlug(field_data, all_data): 
     66    if not slug_re.search(field_data): 
     67        raise ValidationError, _("This value must contain only letters, numbers, underscores or hyphens.") 
     68 
     69def isLowerCase(field_data, all_data): 
     70    if field_data.lower() != field_data: 
     71        raise ValidationError, _("Uppercase letters are not allowed here.") 
     72 
     73def isUpperCase(field_data, all_data): 
     74    if field_data.upper() != field_data: 
     75        raise ValidationError, _("Lowercase letters are not allowed here.") 
     76 
     77def isCommaSeparatedIntegerList(field_data, all_data): 
     78    for supposed_int in field_data.split(','): 
     79        try: 
     80            int(supposed_int) 
     81        except ValueError: 
     82            raise ValidationError, _("Enter only digits separated by commas.") 
     83 
     84def isCommaSeparatedEmailList(field_data, all_data): 
     85    """ 
     86    Checks that field_data is a string of e-mail addresses separated by commas. 
     87    Blank field_data values will not throw a validation error, and whitespace 
     88    is allowed around the commas. 
     89    """ 
     90    for supposed_email in field_data.split(','): 
     91        try: 
     92            isValidEmail(supposed_email.strip(), '') 
     93        except ValidationError: 
     94            raise ValidationError, _("Enter valid e-mail addresses separated by commas.") 
     95 
     96def isValidIPAddress4(field_data, all_data): 
     97    if not ip4_re.search(field_data): 
     98        raise ValidationError, _("Please enter a valid IP address.") 
     99 
     100def isNotEmpty(field_data, all_data): 
     101    if field_data.strip() == '': 
     102        raise ValidationError, _("Empty values are not allowed here.") 
     103 
     104def isOnlyDigits(field_data, all_data): 
     105    if not field_data.isdigit(): 
     106        raise ValidationError, _("Non-numeric characters aren't allowed here.") 
     107 
     108def isNotOnlyDigits(field_data, all_data): 
     109    if field_data.isdigit(): 
     110        raise ValidationError, _("This value can't be comprised solely of digits.") 
     111 
     112def isInteger(field_data, all_data): 
     113    # This differs from isOnlyDigits because this accepts the negative sign 
     114    if not integer_re.search(field_data): 
     115        raise ValidationError, _("Enter a whole number.") 
     116 
     117def isOnlyLetters(field_data, all_data): 
     118    if not field_data.isalpha(): 
     119        raise ValidationError, _("Only alphabetical characters are allowed here.") 
     120 
     121def _isValidDate(date_string): 
     122    """ 
     123    A helper function used by isValidANSIDate and isValidANSIDatetime to 
     124    check if the date is valid.  The date string is assumed to already be in 
     125    YYYY-MM-DD format. 
     126    """ 
     127    from datetime import date 
     128    # Could use time.strptime here and catch errors, but datetime.date below 
     129    # produces much friendlier error messages. 
     130    year, month, day = map(int, date_string.split('-')) 
     131    # This check is needed because strftime is used when saving the date 
     132    # value to the database, and strftime requires that the year be >=1900. 
     133    if year < 1900: 
     134        raise ValidationError, _('Year must be 1900 or later.') 
     135    try: 
     136        date(year, month, day) 
     137    except ValueError, e: 
     138        msg = _('Invalid date: %s') % _(str(e)) 
     139        raise ValidationError, msg 
     140 
     141def isValidANSIDate(field_data, all_data): 
     142    if not ansi_date_re.search(field_data): 
     143        raise ValidationError, _('Enter a valid date in YYYY-MM-DD format.') 
     144    _isValidDate(field_data) 
     145 
     146def isValidANSITime(field_data, all_data): 
     147    if not ansi_time_re.search(field_data): 
     148        raise ValidationError, _('Enter a valid time in HH:MM format.') 
     149 
     150def isValidANSIDatetime(field_data, all_data): 
     151    if not ansi_datetime_re.search(field_data): 
     152        raise ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM format.') 
     153    _isValidDate(field_data.split()[0]) 
     154 
     155def isValidEmail(field_data, all_data): 
     156    if not email_re.search(field_data): 
     157        raise ValidationError, _('Enter a valid e-mail address.') 
     158 
     159def isValidImage(field_data, all_data): 
     160    """ 
     161    Checks that the file-upload field data contains a valid image (GIF, JPG, 
     162    PNG, possibly others -- whatever the Python Imaging Library supports). 
     163    """ 
     164    from PIL import Image 
     165    from cStringIO import StringIO 
     166    try: 
     167        content = field_data['content'] 
     168    except TypeError: 
     169        raise ValidationError, _("No file was submitted. Check the encoding type on the form.") 
     170    try: 
     171        # load() is the only method that can spot a truncated JPEG, 
     172        #  but it cannot be called sanely after verify() 
     173        trial_image = Image.open(StringIO(content)) 
     174        trial_image.load() 
     175        # verify() is the only method that can spot a corrupt PNG, 
     176        #  but it must be called immediately after the constructor 
     177        trial_image = Image.open(StringIO(content)) 
     178        trial_image.verify() 
     179    except Exception: # Python Imaging Library doesn't recognize it as an image 
     180        raise ValidationError, _("Upload a valid image. The file you uploaded was either not an image or a corrupted image.") 
     181 
     182def isValidImageURL(field_data, all_data): 
     183    uc = URLMimeTypeCheck(('image/jpeg', 'image/gif', 'image/png')) 
     184    try: 
     185        uc(field_data, all_data) 
     186    except URLMimeTypeCheck.InvalidContentType: 
     187        raise ValidationError, _("The URL %s does not point to a valid image.") % field_data 
     188 
     189def isValidPhone(field_data, all_data): 
     190    if not phone_re.search(field_data): 
     191        raise ValidationError, _('Phone numbers must be in XXX-XXX-XXXX format. "%s" is invalid.') % field_data 
     192 
     193def isValidQuicktimeVideoURL(field_data, all_data): 
     194    "Checks that the given URL is a video that can be played by QuickTime (qt, mpeg)" 
     195    uc = URLMimeTypeCheck(('video/quicktime', 'video/mpeg',)) 
     196    try: 
     197        uc(field_data, all_data) 
     198    except URLMimeTypeCheck.InvalidContentType: 
     199        raise ValidationError, _("The URL %s does not point to a valid QuickTime video.") % field_data 
     200 
     201def isValidURL(field_data, all_data): 
     202    if not url_re.search(field_data): 
     203        raise ValidationError, _("A valid URL is required.") 
     204 
     205def isValidHTML(field_data, all_data): 
     206    import urllib, urllib2 
     207    try: 
     208        u = urllib2.urlopen('http://validator.w3.org/check', urllib.urlencode({'fragment': field_data, 'output': 'xml'})) 
     209    except: 
     210        # Validator or Internet connection is unavailable. Fail silently. 
     211        return 
     212    html_is_valid = (u.headers.get('x-w3c-validator-status', 'Invalid') == 'Valid') 
     213    if html_is_valid: 
     214        return 
     215    from xml.dom.minidom import parseString 
     216    error_messages = [e.firstChild.wholeText for e in parseString(u.read()).getElementsByTagName('messages')[0].getElementsByTagName('msg')] 
     217    raise ValidationError, _("Valid HTML is required. Specific errors are:\n%s") % "\n".join(error_messages) 
     218 
     219def isWellFormedXml(field_data, all_data): 
     220    from xml.dom.minidom import parseString 
     221    try: 
     222        parseString(field_data) 
     223    except Exception, e: # Naked except because we're not sure what will be thrown 
     224        raise ValidationError, _("Badly formed XML: %s") % str(e) 
     225 
     226def isWellFormedXmlFragment(field_data, all_data): 
     227    isWellFormedXml('<root>%s</root>' % field_data, all_data) 
     228 
     229def isExistingURL(field_data, all_data): 
     230    try: 
     231        headers = { 
     232            "Accept" : "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5", 
     233            "Accept-Language" : "en-us,en;q=0.5", 
     234            "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", 
     235            "Connection" : "close", 
     236            "User-Agent": settings.URL_VALIDATOR_USER_AGENT 
     237            } 
     238        req = urllib2.Request(field_data,None, headers) 
     239        u = urllib2.urlopen(req) 
     240    except ValueError: 
     241        raise ValidationError, _("Invalid URL: %s") % field_data 
     242    except urllib2.HTTPError, e: 
     243        # 401s are valid; they just mean authorization is required. 
     244        # 301 and 302 are redirects; they just mean look somewhere else. 
     245        if str(e.code) not in ('401','301','302'): 
     246            raise ValidationError, _("The URL %s is a broken link.") % field_data 
     247    except: # urllib2.URLError, httplib.InvalidURL, etc. 
     248        raise ValidationError, _("The URL %s is a broken link.") % field_data 
     249 
     250def isValidUSState(field_data, all_data): 
     251    "Checks that the given string is a valid two-letter U.S. state abbreviation" 
     252    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'] 
     253    if field_data.upper() not in states: 
     254        raise ValidationError, _("Enter a valid U.S. state abbreviation.") 
     255 
     256def hasNoProfanities(field_data, all_data): 
     257    """ 
     258    Checks that the given string has no profanities in it. This does a simple 
     259    check for whether each profanity exists within the string, so 'fuck' will 
     260    catch 'motherfucker' as well. Raises a ValidationError such as: 
     261        Watch your mouth! The words "f--k" and "s--t" are not allowed here. 
     262    """ 
     263    field_data = field_data.lower() # normalize 
     264    words_seen = [w for w in settings.PROFANITIES_LIST if w in field_data] 
     265    if words_seen: 
     266        from django.utils.text import get_text_list 
     267        plural = len(words_seen) 
     268        raise ValidationError, ungettext("Watch your mouth! The word %s is not allowed here.", 
     269            "Watch your mouth! The words %s are not allowed here.", plural) % \ 
     270            get_text_list(['"%s%s%s"' % (i[0], '-'*(len(i)-2), i[-1]) for i in words_seen], _('and')) 
     271 
     272class AlwaysMatchesOtherField(object): 
     273    def __init__(self, other_field_name, error_message=None): 
     274        self.other = other_field_name 
     275        self.error_message = error_message or lazy_inter(ugettext_lazy("This field must match the '%s' field."), self.other) 
     276        self.always_test = True 
     277 
     278    def __call__(self, field_data, all_data): 
     279        if field_data != all_data[self.other]: 
     280            raise ValidationError, self.error_message 
     281 
     282class ValidateIfOtherFieldEquals(object): 
     283    def __init__(self, other_field, other_value, validator_list): 
     284        self.other_field, self.other_value = other_field, other_value 
     285        self.validator_list = validator_list 
     286        self.always_test = True 
     287 
     288    def __call__(self, field_data, all_data): 
     289        if self.other_field in all_data and all_data[self.other_field] == self.other_value: 
     290            for v in self.validator_list: 
     291                v(field_data, all_data) 
     292 
     293class RequiredIfOtherFieldNotGiven(object): 
     294    def __init__(self, other_field_name, error_message=ugettext_lazy("Please enter something for at least one field.")): 
     295        self.other, self.error_message = other_field_name, error_message 
     296        self.always_test = True 
     297 
     298    def __call__(self, field_data, all_data): 
     299        if not all_data.get(self.other, False) and not field_data: 
     300            raise ValidationError, self.error_message 
     301 
     302class RequiredIfOtherFieldsGiven(object): 
     303    def __init__(self, other_field_names, error_message=ugettext_lazy("Please enter both fields or leave them both empty.")): 
     304        self.other, self.error_message = other_field_names, error_message 
     305        self.always_test = True 
     306 
     307    def __call__(self, field_data, all_data): 
     308        for field in self.other: 
     309            if all_data.get(field, False) and not field_data: 
     310                raise ValidationError, self.error_message 
     311 
     312class RequiredIfOtherFieldGiven(RequiredIfOtherFieldsGiven): 
     313    "Like RequiredIfOtherFieldsGiven, but takes a single field name instead of a list." 
     314    def __init__(self, other_field_name, error_message=ugettext_lazy("Please enter both fields or leave them both empty.")): 
     315        RequiredIfOtherFieldsGiven.__init__(self, [other_field_name], error_message) 
     316 
     317class RequiredIfOtherFieldEquals(object): 
     318    def __init__(self, other_field, other_value, error_message=None, other_label=None): 
     319        self.other_field = other_field 
     320        self.other_value = other_value 
     321        other_label = other_label or other_value 
     322        self.error_message = error_message or lazy_inter(ugettext_lazy("This field must be given if %(field)s is %(value)s"), { 
     323            'field': other_field, 'value': other_label}) 
     324        self.always_test = True 
     325 
     326    def __call__(self, field_data, all_data): 
     327        if self.other_field in all_data and all_data[self.other_field] == self.other_value and not field_data: 
     328            raise ValidationError(self.error_message) 
     329 
     330class RequiredIfOtherFieldDoesNotEqual(object): 
     331    def __init__(self, other_field, other_value, other_label=None, error_message=None): 
     332        self.other_field = other_field 
     333        self.other_value = other_value 
     334        other_label = other_label or other_value 
     335        self.error_message = error_message or lazy_inter(ugettext_lazy("This field must be given if %(field)s is not %(value)s"), { 
     336            'field': other_field, 'value': other_label}) 
     337        self.always_test = True 
     338 
     339    def __call__(self, field_data, all_data): 
     340        if self.other_field in all_data and all_data[self.other_field] != self.other_value and not field_data: 
     341            raise ValidationError(self.error_message) 
     342 
     343class IsLessThanOtherField(object): 
     344    def __init__(self, other_field_name, error_message): 
     345        self.other, self.error_message = other_field_name, error_message 
     346 
     347    def __call__(self, field_data, all_data): 
     348        if field_data > all_data[self.other]: 
     349            raise ValidationError, self.error_message 
     350 
     351class UniqueAmongstFieldsWithPrefix(object): 
     352    def __init__(self, field_name, prefix, error_message): 
     353        self.field_name, self.prefix = field_name, prefix 
     354        self.error_message = error_message or ugettext_lazy("Duplicate values are not allowed.") 
     355 
     356    def __call__(self, field_data, all_data): 
     357        for field_name, value in all_data.items(): 
     358            if field_name != self.field_name and value == field_data: 
     359                raise ValidationError, self.error_message 
     360 
     361class NumberIsInRange(object): 
     362    """ 
     363    Validator that tests if a value is in a range (inclusive). 
     364    """ 
     365    def __init__(self, lower=None, upper=None, error_message=''): 
     366        self.lower, self.upper = lower, upper 
     367        if not error_message: 
     368            if lower and upper: 
     369                 self.error_message = _("This value must be between %(lower)s and %(upper)s.") % {'lower': lower, 'upper': upper} 
     370            elif lower: 
     371                self.error_message = _("This value must be at least %s.") % lower 
     372            elif upper: 
     373                self.error_message = _("This value must be no more than %s.") % upper 
     374        else: 
     375            self.error_message = error_message 
     376 
     377    def __call__(self, field_data, all_data): 
     378        # Try to make the value numeric. If this fails, we assume another 
     379        # validator will catch the problem. 
     380        try: 
     381            val = float(field_data) 
     382        except ValueError: 
     383            return 
     384 
     385        # Now validate 
     386        if self.lower and self.upper and (val < self.lower or val > self.upper): 
     387            raise ValidationError(self.error_message) 
     388        elif self.lower and val < self.lower: 
     389            raise ValidationError(self.error_message) 
     390        elif self.upper and val > self.upper: 
     391            raise ValidationError(self.error_message) 
     392 
     393class IsAPowerOf(object): 
     394    """ 
     395    Usage: If you create an instance of the IsPowerOf validator: 
     396        v = IsAPowerOf(2) 
     397     
     398    The following calls will succeed: 
     399        v(4, None)  
     400        v(8, None) 
     401        v(16, None) 
     402     
     403    But this call: 
     404        v(17, None) 
     405    will raise "django.core.validators.ValidationError: ['This value must be a power of 2.']" 
     406    """ 
     407    def __init__(self, power_of): 
     408        self.power_of = power_of 
     409 
     410    def __call__(self, field_data, all_data): 
     411        from math import log 
     412        val = log(int(field_data)) / log(self.power_of) 
     413        if val != int(val): 
     414            raise ValidationError, _("This value must be a power of %s.") % self.power_of 
     415 
     416class IsValidDecimal(object): 
     417    def __init__(self, max_digits, decimal_places): 
     418        self.max_digits, self.decimal_places = max_digits, decimal_places 
     419 
     420    def __call__(self, field_data, all_data): 
     421        try: 
     422            val = Decimal(field_data) 
     423        except DecimalException: 
     424            raise ValidationError, _("Please enter a valid decimal number.") 
     425 
     426        pieces = str(val).lstrip("-").split('.') 
     427        decimals = (len(pieces) == 2) and len(pieces[1]) or 0 
     428        digits = len(pieces[0]) 
     429 
     430        if digits + decimals > self.max_digits: 
     431            raise ValidationError, ungettext("Please enter a valid decimal number with at most %s total digit.", 
     432                "Please enter a valid decimal number with at most %s total digits.", self.max_digits) % self.max_digits 
     433        if digits > (self.max_digits - self.decimal_places): 
     434            raise ValidationError, ungettext( "Please enter a valid decimal number with a whole part of at most %s digit.", 
     435                "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) 
     436        if decimals > self.decimal_places: 
     437            raise ValidationError, ungettext("Please enter a valid decimal number with at most %s decimal place.", 
     438                "Please enter a valid decimal number with at most %s decimal places.", self.decimal_places) % self.decimal_places 
     439 
     440def isValidFloat(field_data, all_data): 
     441    data = smart_str(field_data) 
     442    try: 
     443        float(data) 
     444    except ValueError: 
     445        raise ValidationError, _("Please enter a valid floating point number.") 
     446 
     447class HasAllowableSize(object): 
     448    """ 
     449    Checks that the file-upload field data is a certain size. min_size and 
     450    max_size are measurements in bytes. 
     451    """ 
     452    def __init__(self, min_size=None, max_size=None, min_error_message=None, max_error_message=None): 
     453        self.min_size, self.max_size = min_size, max_size 
     454        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) 
     455        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) 
     456 
     457    def __call__(self, field_data, all_data): 
     458        try: 
     459            content = field_data['content'] 
     460        except TypeError: 
     461            raise ValidationError, ugettext_lazy("No file was submitted. Check the encoding type on the form.") 
     462        if self.min_size is not None and len(content) < self.min_size: 
     463            raise ValidationError, self.min_error_message 
     464        if self.max_size is not None and len(content) > self.max_size: 
     465            raise ValidationError, self.max_error_message 
     466 
     467class MatchesRegularExpression(object): 
     468    """ 
     469    Checks that the field matches the given regular-expression. The regex 
     470    should be in string format, not already compiled. 
     471    """ 
     472    def __init__(self, regexp, error_message=ugettext_lazy("The format for this field is wrong.")): 
     473        self.regexp = re.compile(regexp) 
     474        self.error_message = error_message 
     475 
     476    def __call__(self, field_data, all_data): 
     477        if not self.regexp.search(field_data): 
     478            raise ValidationError(self.error_message) 
     479 
     480class AnyValidator(object): 
     481    """ 
     482    This validator tries all given validators. If any one of them succeeds, 
     483    validation passes. If none of them succeeds, the given message is thrown 
     484    as a validation error. The message is rather unspecific, so it's best to 
     485    specify one on instantiation. 
     486    """ 
     487    def __init__(self, validator_list=None, error_message=ugettext_lazy("This field is invalid.")): 
     488        if validator_list is None: validator_list = [] 
     489        self.validator_list = validator_list 
     490        self.error_message = error_message 
     491        for v in validator_list: 
     492            if hasattr(v, 'always_test'): 
     493                self.always_test = True 
     494 
     495    def __call__(self, field_data, all_data): 
     496        for v in self.validator_list: 
     497            try: 
     498                v(field_data, all_data) 
     499                return 
     500            except ValidationError, e: 
     501                pass 
     502        raise ValidationError(self.error_message) 
     503 
     504class URLMimeTypeCheck(object): 
     505    "Checks that the provided URL points to a document with a listed mime type" 
     506    class CouldNotRetrieve(ValidationError): 
     507        pass 
     508    class InvalidContentType(ValidationError): 
     509        pass 
     510 
     511    def __init__(self, mime_type_list): 
     512        self.mime_type_list = mime_type_list 
     513 
     514    def __call__(self, field_data, all_data): 
     515        import urllib2 
     516        try: 
     517            isValidURL(field_data, all_data) 
     518        except ValidationError: 
     519            raise 
     520        try: 
     521            info = urllib2.urlopen(field_data).info() 
     522        except (urllib2.HTTPError, urllib2.URLError): 
     523            raise URLMimeTypeCheck.CouldNotRetrieve, _("Could not retrieve anything from %s.") % field_data 
     524        content_type = info['content-type'] 
     525        if content_type not in self.mime_type_list: 
     526            raise URLMimeTypeCheck.InvalidContentType, _("The URL %(url)s returned the invalid Content-Type header '%(contenttype)s'.") % { 
     527                'url': field_data, 'contenttype': content_type} 
     528 
     529class RelaxNGCompact(object): 
     530    "Validate against a Relax NG compact schema" 
     531    def __init__(self, schema_path, additional_root_element=None): 
     532        self.schema_path = schema_path 
     533        self.additional_root_element = additional_root_element 
     534 
     535    def __call__(self, field_data, all_data): 
     536        import os, tempfile 
     537        if self.additional_root_element: 
     538            field_data = '<%(are)s>%(data)s\n</%(are)s>' % { 
     539                'are': self.additional_root_element, 
     540                'data': field_data 
     541            } 
     542        filename = tempfile.mktemp() # Insecure, but nothing else worked 
     543        fp = open(filename, 'w') 
     544        fp.write(field_data) 
     545        fp.close() 
     546        if not os.path.exists(settings.JING_PATH): 
     547            raise Exception, "%s not found!" % settings.JING_PATH 
     548        p = os.popen('%s -c %s %s' % (settings.JING_PATH, self.schema_path, filename)) 
     549        errors = [line.strip() for line in p.readlines()] 
     550        p.close() 
     551        os.unlink(filename) 
     552        display_errors = [] 
     553        lines = field_data.split('\n') 
     554        for error in errors: 
     555            ignored, line, level, message = error.split(':', 3) 
     556            # Scrape the Jing error messages to reword them more nicely. 
     557            m = re.search(r'Expected "(.*?)" to terminate element starting on line (\d+)', message) 
     558            if m: 
     559                display_errors.append(_('Please close the unclosed %(tag)s tag from line %(line)s. (Line starts with "%(start)s".)') % \ 
     560                    {'tag':m.group(1).replace('/', ''), 'line':m.group(2), 'start':lines[int(m.group(2)) - 1][:30]}) 
     561                continue 
     562            if message.strip() == 'text not allowed here': 
     563                display_errors.append(_('Some text starting on line %(line)s is not allowed in that context. (Line starts with "%(start)s".)') % \ 
     564                    {'line':line, 'start':lines[int(line) - 1][:30]}) 
     565                continue 
     566            m = re.search(r'\s*attribute "(.*?)" not allowed at this point; ignored', message) 
     567            if m: 
     568                display_errors.append(_('"%(attr)s" on line %(line)s is an invalid attribute. (Line starts with "%(start)s".)') % \ 
     569                    {'attr':m.group(1), 'line':line, 'start':lines[int(line) - 1][:30]}) 
     570                continue 
     571            m = re.search(r'\s*unknown element "(.*?)"', message) 
     572            if m: 
     573                display_errors.append(_('"<%(tag)s>" on line %(line)s is an invalid tag. (Line starts with "%(start)s".)') % \ 
     574                    {'tag':m.group(1), 'line':line, 'start':lines[int(line) - 1][:30]}) 
     575                continue 
     576            if message.strip() == 'required attributes missing': 
     577                display_errors.append(_('A tag on line %(line)s is missing one or more required attributes. (Line starts with "%(start)s".)') % \ 
     578                    {'line':line, 'start':lines[int(line) - 1][:30]}) 
     579                continue 
     580            m = re.search(r'\s*bad value for attribute "(.*?)"', message) 
     581            if m: 
     582                display_errors.append(_('The "%(attr)s" attribute on line %(line)s has an invalid value. (Line starts with "%(start)s".)') % \ 
     583                    {'attr':m.group(1), 'line':line, 'start':lines[int(line) - 1][:30]}) 
     584                continue 
     585            # Failing all those checks, use the default error message. 
     586            display_error = 'Line %s: %s [%s]' % (line, message, level.strip()) 
     587            display_errors.append(display_error) 
     588        if len(display_errors) > 0: 
     589            raise ValidationError, display_errors 
  • a/tests/modeltests/manipulators/models.py

    old new  
    9999datetime.date(2005, 2, 13) 
    100100 
    101101# Test isValidFloat Unicode coercion 
    102 >>> from django.core.validators import isValidFloat, ValidationError 
     102>>> from django.oldforms.validators import isValidFloat, ValidationError 
    103103>>> try: isValidFloat(u"ä", None) 
    104104... except ValidationError: pass 
    105105"""} 
  • a/tests/modeltests/model_forms/models.py

    old new  
    478478...         model = Article 
    479479>>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01', 
    480480...     'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']}) 
     481>>> f.is_valid() 
     482True 
    481483>>> new_art = f.save() 
    482484>>> new_art.id 
    4834852 
     
    705707>>> f.clean('hello') 
    706708Traceback (most recent call last): 
    707709... 
    708 ValidationError: [u'Enter a list of values.'] 
     710TypeCoercionError: [u'Enter a list of values.'] 
    709711 
    710712# Add a Category object *after* the ModelMultipleChoiceField has already been 
    711713# instantiated. This proves clean() checks the database during clean() rather 
     
    829831>>> instance.delete() 
    830832 
    831833# Test the non-required FileField 
    832  
     834# It should fail since the field IS required on the model 
    833835>>> f = TextFileForm(data={'description': u'Assistance'}) 
    834836>>> f.fields['file'].required = False 
    835837>>> f.is_valid() 
    836 True 
    837 >>> instance = f.save() 
    838 >>> instance.file 
    839 '' 
     838False 
     839>>> f.errors 
     840{'file': [u'This field is required.']} 
    840841 
    841842>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test3.txt', 'content': 'hello world'}}, instance=instance) 
    842843>>> f.is_valid() 
     
    899900>>> f = ImageFileForm(data={'description': u'Test'}) 
    900901>>> f.fields['image'].required = False 
    901902>>> f.is_valid() 
    902 Tru
    903 >>> instance = f.save() 
    904 >>> instance.image 
    905 '' 
     903Fals
     904>>> f.errors 
     905{'image': [u'This field is required.']} 
     906 
    906907 
    907908>>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': {'filename': 'test3.png', 'content': image_data}}, instance=instance) 
    908909>>> f.is_valid() 
  • a/tests/modeltests/validation/models.py

    old new  
    33 
    44This is an experimental feature! 
    55 
    6 Each model instance has a validate() method that returns a dictionary of 
     6Each model instance has a clean() method that returns a dictionary of 
    77validation errors in the instance's fields. This method has a side effect 
    88of converting each field to its appropriate Python data type. 
    99""" 
     
    1515    name = models.CharField(max_length=20) 
    1616    birthdate = models.DateField() 
    1717    favorite_moment = models.DateTimeField() 
    18     email = models.EmailField() 
    19  
     18    email = models.EmailField(unique=True) 
     19     
     20    class Meta: 
     21        unique_together = (('name', 'is_child'),) 
    2022    def __unicode__(self): 
    2123        return self.name 
    2224 
     
    3032...     'favorite_moment': datetime.datetime(2002, 4, 3, 13, 23), 
    3133...     'email': 'john@example.com' 
    3234... } 
    33 >>> p = Person(**valid_params) 
    34 >>> p.validate() 
    35 {} 
    36  
    37 >>> p = Person(**dict(valid_params, id='23')) 
    38 >>> p.validate() 
    39 {} 
     35>>> p = Person(**dict(valid_params, email='john@e.com', name='Jack')) 
     36>>> p.clean() 
     37>>> p.save() 
     38 
     39>>> p = Person(**dict(valid_params, email='john@e.com')) 
     40>>> p.clean() 
     41Traceback (most recent call last): 
     42... 
     43ValidationError: {'email': [u'This field must be unique']} 
     44 
     45>>> p = Person(**dict(valid_params, id='23', name='Jack')) 
     46>>> p.clean() 
     47Traceback (most recent call last): 
     48... 
     49ValidationError: {'__all__': u'Fields name, is_child must be unique.'} 
    4050>>> p.id 
    415123 
    4252 
    43 >>> p = Person(**dict(valid_params, id='foo')) 
    44 >>> p.validate()['id'] 
    45 [u'This value must be an integer.'] 
     53# when type coercion fails, no other validation is done 
     54>>> p = Person(**dict(valid_params, email='john@e.com', id='foo')) 
     55>>> p.clean() 
     56Traceback (most recent call last): 
     57... 
     58ValidationError: {'id': [u'This value must be an integer.']} 
    4659 
    4760>>> p = Person(**dict(valid_params, id=None)) 
    48 >>> p.validate() 
    49 {} 
     61>>> p.clean() 
    5062>>> repr(p.id) 
    5163'None' 
    5264 
    5365>>> p = Person(**dict(valid_params, is_child='t')) 
    54 >>> p.validate() 
    55 {} 
     66>>> p.clean() 
    5667>>> p.is_child 
    5768True 
    5869 
    5970>>> p = Person(**dict(valid_params, is_child='f')) 
    60 >>> p.validate() 
    61 {} 
     71>>> p.clean() 
    6272>>> p.is_child 
    6373False 
    6474 
    6575>>> p = Person(**dict(valid_params, is_child=True)) 
    66 >>> p.validate() 
    67 {} 
     76>>> p.clean() 
    6877>>> p.is_child 
    6978True 
    7079 
    7180>>> p = Person(**dict(valid_params, is_child=False)) 
    72 >>> p.validate() 
    73 {} 
     81>>> p.clean() 
    7482>>> p.is_child 
    7583False 
    7684 
    7785>>> p = Person(**dict(valid_params, is_child='foo')) 
    78 >>> p.validate()['is_child'] 
    79 [u'This value must be either True or False.'] 
     86>>> p.clean() 
     87Traceback (most recent call last): 
     88... 
     89ValidationError: {'is_child': [u'This value must be either True or False.']} 
    8090 
    8191>>> p = Person(**dict(valid_params, name=u'Jose')) 
    82 >>> p.validate() 
    83 {} 
     92>>> p.clean() 
    8493>>> p.name 
    8594u'Jose' 
    8695 
    8796>>> p = Person(**dict(valid_params, name=227)) 
    88 >>> p.validate() 
    89 {} 
     97>>> p.clean() 
    9098>>> p.name 
    9199u'227' 
    92100 
    93101>>> p = Person(**dict(valid_params, birthdate=datetime.date(2000, 5, 3))) 
    94 >>> p.validate() 
    95 {} 
     102>>> p.clean() 
    96103>>> p.birthdate 
    97104datetime.date(2000, 5, 3) 
    98105 
    99106>>> p = Person(**dict(valid_params, birthdate=datetime.datetime(2000, 5, 3))) 
    100 >>> p.validate() 
    101 {} 
     107>>> p.clean() 
    102108>>> p.birthdate 
    103109datetime.date(2000, 5, 3) 
    104110 
    105111>>> p = Person(**dict(valid_params, birthdate='2000-05-03')) 
    106 >>> p.validate() 
    107 {} 
     112>>> p.clean() 
    108113>>> p.birthdate 
    109114datetime.date(2000, 5, 3) 
    110115 
    111116>>> p = Person(**dict(valid_params, birthdate='2000-5-3')) 
    112 >>> p.validate() 
    113 {} 
     117>>> p.clean() 
    114118>>> p.birthdate 
    115119datetime.date(2000, 5, 3) 
    116120 
    117121>>> p = Person(**dict(valid_params, birthdate='foo')) 
    118 >>> p.validate()['birthdate'] 
    119 [u'Enter a valid date in YYYY-MM-DD format.'] 
     122>>> p.clean() 
     123Traceback (most recent call last): 
     124... 
     125ValidationError: {'birthdate': [u'Enter a valid date in YYYY-MM-DD format.']} 
    120126 
    121127>>> p = Person(**dict(valid_params, favorite_moment=datetime.datetime(2002, 4, 3, 13, 23))) 
    122 >>> p.validate() 
    123 {} 
     128>>> p.clean() 
    124129>>> p.favorite_moment 
    125130datetime.datetime(2002, 4, 3, 13, 23) 
    126131 
    127132>>> p = Person(**dict(valid_params, favorite_moment=datetime.datetime(2002, 4, 3))) 
    128 >>> p.validate() 
    129 {} 
     133>>> p.clean() 
    130134>>> p.favorite_moment 
    131135datetime.datetime(2002, 4, 3, 0, 0) 
    132136 
    133137>>> p = Person(**dict(valid_params, email='john@example.com')) 
    134 >>> p.validate() 
    135 {} 
     138>>> p.clean() 
    136139>>> p.email 
    137140'john@example.com' 
    138141 
    139142>>> p = Person(**dict(valid_params, email=u'john@example.com')) 
    140 >>> p.validate() 
    141 {} 
     143>>> p.clean() 
    142144>>> p.email 
    143145u'john@example.com' 
    144146 
    145147>>> p = Person(**dict(valid_params, email=22)) 
    146 >>> p.validate()['email'] 
    147 [u'Enter a valid e-mail address.'] 
     148>>> p.clean() 
     149Traceback (most recent call last): 
     150... 
     151ValidationError: {'email': [u'Enter a valid e-mail address.']} 
    148152 
    149153# Make sure that Date and DateTime return validation errors and don't raise Python errors. 
    150 >>> p = Person(name='John Doe', is_child=True, email='abc@def.com') 
    151 >>> errors = p.validate() 
    152 >>> errors['favorite_moment'] 
     154>>> from django.core.validation import ValidationError 
     155>>> try: 
     156...     Person(name='John Doe', is_child=True, email='abc@def.com').clean() 
     157... except ValidationError, e: 
     158...     e.message_dict['favorite_moment'] 
     159...     e.message_dict['birthdate'] 
    153160[u'This field is required.'] 
    154 >>> errors['birthdate'] 
    155161[u'This field is required.'] 
    156162 
    157163"""} 
  • /dev/null

    old new  
     1# A models.py so that tests run. 
     2 
  • /dev/null

    old new  
     1tests = r""" 
     2################### 
     3# ValidationError # 
     4################### 
     5>>> from django.core.exceptions import ValidationError 
     6>>> from django.utils.translation import ugettext_lazy 
     7 
     8# Can take a string. 
     9>>> print ValidationError("There was an error.").messages 
     10<ul class="errorlist"><li>There was an error.</li></ul> 
     11 
     12# Can take a unicode string. 
     13>>> print ValidationError(u"Not \u03C0.").messages 
     14<ul class="errorlist"><li>Not π.</li></ul> 
     15 
     16# Can take a lazy string. 
     17>>> print ValidationError(ugettext_lazy("Error.")).messages 
     18<ul class="errorlist"><li>Error.</li></ul> 
     19 
     20# Can take a list. 
     21>>> print ValidationError(["Error one.", "Error two."]).messages 
     22<ul class="errorlist"><li>Error one.</li><li>Error two.</li></ul> 
     23 
     24# Can take a mixture in a list. 
     25>>> print ValidationError(["First error.", u"Not \u03C0.", ugettext_lazy("Error.")]).messages 
     26<ul class="errorlist"><li>First error.</li><li>Not π.</li><li>Error.</li></ul> 
     27 
     28>>> class VeryBadError: 
     29...     def __unicode__(self): return u"A very bad error." 
     30 
     31# Can take a non-string. 
     32>>> print ValidationError(VeryBadError()).messages 
     33<ul class="errorlist"><li>A very bad error.</li></ul> 
     34""" 
     35 
  • a/tests/regressiontests/forms/error_messages.py

    old new  
    3535>>> f.clean('abc') 
    3636Traceback (most recent call last): 
    3737... 
    38 ValidationError: [u'INVALID'] 
     38TypeCoercionError: [u'INVALID'] 
    3939>>> f.clean('4') 
    4040Traceback (most recent call last): 
    4141... 
     
    5959>>> f.clean('abc') 
    6060Traceback (most recent call last): 
    6161... 
    62 ValidationError: [u'INVALID'] 
     62TypeCoercionError: [u'INVALID'] 
    6363>>> f.clean('4') 
    6464Traceback (most recent call last): 
    6565... 
     
    8787>>> f.clean('abc') 
    8888Traceback (most recent call last): 
    8989... 
    90 ValidationError: [u'INVALID'] 
     90TypeCoercionError: [u'INVALID'] 
    9191>>> f.clean('4') 
    9292Traceback (most recent call last): 
    9393... 
     
    121121>>> f.clean('abc') 
    122122Traceback (most recent call last): 
    123123... 
    124 ValidationError: [u'INVALID'] 
     124TypeCoercionError: [u'INVALID'] 
    125125 
    126126# TimeField ################################################################### 
    127127 
     
    135135>>> f.clean('abc') 
    136136Traceback (most recent call last): 
    137137... 
    138 ValidationError: [u'INVALID'] 
     138TypeCoercionError: [u'INVALID'] 
    139139 
    140140# DateTimeField ############################################################### 
    141141 
     
    149149>>> f.clean('abc') 
    150150Traceback (most recent call last): 
    151151... 
    152 ValidationError: [u'INVALID'] 
     152TypeCoercionError: [u'INVALID'] 
    153153 
    154154# RegexField ################################################################## 
    155155 
     
    203203 
    204204>>> e = {'required': 'REQUIRED'} 
    205205>>> e['invalid'] = 'INVALID' 
    206 >>> e['missing'] = 'MISSING' 
    207206>>> e['empty'] = 'EMPTY FILE' 
    208207>>> f = FileField(error_messages=e) 
    209208>>> f.clean('') 
     
    213212>>> f.clean('abc') 
    214213Traceback (most recent call last): 
    215214... 
    216 ValidationError: [u'INVALID'] 
     215TypeCoercionError: [u'INVALID'] 
    217216>>> f.clean({}) 
    218217Traceback (most recent call last): 
    219218... 
    220 ValidationError: [u'MISSING'] 
     219ValidationError: [u'REQUIRED'] 
    221220>>> f.clean({'filename': 'name', 'content':''}) 
    222221Traceback (most recent call last): 
    223222... 
     
    278277>>> f.clean('b') 
    279278Traceback (most recent call last): 
    280279... 
    281 ValidationError: [u'NOT A LIST'] 
     280TypeCoercionError: [u'NOT A LIST'] 
    282281>>> f.clean(['b']) 
    283282Traceback (most recent call last): 
    284283... 
     
    352351>>> f.clean('3') 
    353352Traceback (most recent call last): 
    354353... 
    355 ValidationError: [u'NOT A LIST OF VALUES'] 
     354TypeCoercionError: [u'NOT A LIST OF VALUES'] 
    356355>>> f.clean(['4']) 
    357356Traceback (most recent call last): 
    358357... 
  • a/tests/regressiontests/forms/extra.py

    old new  
    424424# Test overriding ErrorList in a form # 
    425425####################################### 
    426426 
    427 >>> from django.newforms.util import ErrorList 
     427>>> from django.core.validation import ErrorList 
    428428>>> class DivErrorList(ErrorList): 
    429429...     def __unicode__(self): 
    430430...         return self.as_divs() 
  • a/tests/regressiontests/forms/fields.py

    old new  
    3131             field name, if the Field is part of a Form. 
    3232    initial -- A value to use in this Field's initial display. This value is 
    3333               *not* used as a fallback if data isn't given. 
     34    validators -- Optional list of additional validator functions 
    3435 
    3536Other than that, the Field subclasses have class-specific options for 
    3637__init__(). For example, CharField has a max_length option. 
     
    103104>>> f.clean('1234567890a') 
    104105u'1234567890a' 
    105106 
     107# Custom validator functions ################################################## 
     108 
     109>>> def validator(value): raise ValidationError('validator failed') 
     110>>> f = CharField(min_length=10, validators=[validator]) 
     111>>> f.clean('aa') 
     112Traceback (most recent call last): 
     113... 
     114ValidationError: [u'validator failed'] 
     115 
     116>>> def validator2(value): raise ValidationError('validator2 failed') 
     117>>> f = CharField(min_length=10, validators=[validator, validator, validator2]) 
     118>>> f.clean('aa') 
     119Traceback (most recent call last): 
     120... 
     121ValidationError: [u'validator failed', u'validator failed', u'validator2 failed'] 
     122 
     123>>> class MyCharField(CharField): 
     124...     validators = [validator] 
     125>>> f = MyCharField() 
     126>>> f.clean('aa') 
     127Traceback (most recent call last): 
     128... 
     129ValidationError: [u'validator failed'] 
     130 
    106131# IntegerField ################################################################ 
    107132 
    108133>>> f = IntegerField() 
     
    123148>>> f.clean('a') 
    124149Traceback (most recent call last): 
    125150... 
    126 ValidationError: [u'Enter a whole number.'] 
     151TypeCoercionError: [u'Enter a whole number.'] 
    127152>>> f.clean(42) 
    12815342 
    129154>>> f.clean(3.14) 
    130155Traceback (most recent call last): 
    131156... 
    132 ValidationError: [u'Enter a whole number.'] 
     157TypeCoercionError: [u'Enter a whole number.'] 
    133158>>> f.clean('1 ') 
    1341591 
    135160>>> f.clean(' 1') 
     
    139164>>> f.clean('1a') 
    140165Traceback (most recent call last): 
    141166... 
    142 ValidationError: [u'Enter a whole number.'] 
     167TypeCoercionError: [u'Enter a whole number.'] 
    143168 
    144169>>> f = IntegerField(required=False) 
    145170>>> f.clean('') 
     
    157182>>> f.clean('a') 
    158183Traceback (most recent call last): 
    159184... 
    160 ValidationError: [u'Enter a whole number.'] 
     185TypeCoercionError: [u'Enter a whole number.'] 
    161186>>> f.clean('1 ') 
    1621871 
    163188>>> f.clean(' 1') 
     
    167192>>> f.clean('1a') 
    168193Traceback (most recent call last): 
    169194... 
    170 ValidationError: [u'Enter a whole number.'] 
     195TypeCoercionError: [u'Enter a whole number.'] 
    171196 
    172197IntegerField accepts an optional max_value parameter: 
    173198>>> f = IntegerField(max_value=10) 
     
    260285>>> f.clean('a') 
    261286Traceback (most recent call last): 
    262287... 
    263 ValidationError: [u'Enter a number.'] 
     288TypeCoercionError: [u'Enter a number.'] 
    264289>>> f.clean('1.0 ') 
    2652901.0 
    266291>>> f.clean(' 1.0') 
     
    270295>>> f.clean('1.0a') 
    271296Traceback (most recent call last): 
    272297... 
    273 ValidationError: [u'Enter a number.'] 
     298TypeCoercionError: [u'Enter a number.'] 
    274299 
    275300>>> f = FloatField(required=False) 
    276301>>> f.clean('') 
     
    322347>>> f.clean('a') 
    323348Traceback (most recent call last): 
    324349... 
    325 ValidationError: [u'Enter a number.'] 
     350TypeCoercionError: [u'Enter a number.'] 
    326351>>> f.clean(u'łąść') 
    327352Traceback (most recent call last): 
    328353... 
    329 ValidationError: [u'Enter a number.'] 
     354TypeCoercionError: [u'Enter a number.'] 
    330355>>> f.clean('1.0 ') 
    331356Decimal("1.0") 
    332357>>> f.clean(' 1.0') 
     
    336361>>> f.clean('1.0a') 
    337362Traceback (most recent call last): 
    338363... 
    339 ValidationError: [u'Enter a number.'] 
     364TypeCoercionError: [u'Enter a number.'] 
    340365>>> f.clean('123.45') 
    341366Traceback (most recent call last): 
    342367... 
     
    372397>>> f.clean('--0.12') 
    373398Traceback (most recent call last): 
    374399... 
    375 ValidationError: [u'Enter a number.'] 
     400TypeCoercionError: [u'Enter a number.'] 
    376401 
    377402>>> f = DecimalField(max_digits=4, decimal_places=2, required=False) 
    378403>>> f.clean('') 
     
    433458>>> f.clean('2006-4-31') 
    434459Traceback (most recent call last): 
    435460... 
    436 ValidationError: [u'Enter a valid date.'] 
     461TypeCoercionError: [u'Enter a valid date.'] 
    437462>>> f.clean('200a-10-25') 
    438463Traceback (most recent call last): 
    439464... 
    440 ValidationError: [u'Enter a valid date.'] 
     465TypeCoercionError: [u'Enter a valid date.'] 
    441466>>> f.clean('25/10/06') 
    442467Traceback (most recent call last): 
    443468... 
    444 ValidationError: [u'Enter a valid date.'] 
     469TypeCoercionError: [u'Enter a valid date.'] 
    445470>>> f.clean(None) 
    446471Traceback (most recent call last): 
    447472... 
     
    469494>>> f.clean('2006-10-25') 
    470495Traceback (most recent call last): 
    471496... 
    472 ValidationError: [u'Enter a valid date.'] 
     497TypeCoercionError: [u'Enter a valid date.'] 
    473498>>> f.clean('10/25/2006') 
    474499Traceback (most recent call last): 
    475500... 
    476 ValidationError: [u'Enter a valid date.'] 
     501TypeCoercionError: [u'Enter a valid date.'] 
    477502>>> f.clean('10/25/06') 
    478503Traceback (most recent call last): 
    479504... 
    480 ValidationError: [u'Enter a valid date.'] 
     505TypeCoercionError: [u'Enter a valid date.'] 
    481506 
    482507# TimeField ################################################################### 
    483508 
     
    494519>>> f.clean('hello') 
    495520Traceback (most recent call last): 
    496521... 
    497 ValidationError: [u'Enter a valid time.'] 
     522TypeCoercionError: [u'Enter a valid time.'] 
    498523>>> f.clean('1:24 p.m.') 
    499524Traceback (most recent call last): 
    500525... 
    501 ValidationError: [u'Enter a valid time.'] 
     526TypeCoercionError: [u'Enter a valid time.'] 
    502527 
    503528TimeField accepts an optional input_formats parameter: 
    504529>>> f = TimeField(input_formats=['%I:%M %p']) 
     
    516541>>> f.clean('14:30:45') 
    517542Traceback (most recent call last): 
    518543... 
    519 ValidationError: [u'Enter a valid time.'] 
     544TypeCoercionError: [u'Enter a valid time.'] 
    520545 
    521546# DateTimeField ############################################################### 
    522547 
     
    557582>>> f.clean('hello') 
    558583Traceback (most recent call last): 
    559584... 
    560 ValidationError: [u'Enter a valid date/time.'] 
     585TypeCoercionError: [u'Enter a valid date/time.'] 
    561586>>> f.clean('2006-10-25 4:30 p.m.') 
    562587Traceback (most recent call last): 
    563588... 
    564 ValidationError: [u'Enter a valid date/time.'] 
     589TypeCoercionError: [u'Enter a valid date/time.'] 
    565590 
    566591DateField accepts an optional input_formats parameter: 
    567592>>> f = DateTimeField(input_formats=['%Y %m %d %I:%M %p']) 
     
    581606>>> f.clean('2006-10-25 14:30:45') 
    582607Traceback (most recent call last): 
    583608... 
    584 ValidationError: [u'Enter a valid date/time.'] 
     609TypeCoercionError: [u'Enter a valid date/time.'] 
    585610 
    586611>>> f = DateTimeField(required=False) 
    587612>>> f.clean(None) 
     
    773798>>> f.clean({}) 
    774799Traceback (most recent call last): 
    775800... 
    776 ValidationError: [u'No file was submitted.'] 
     801ValidationError: [u'This field is required.'] 
    777802 
    778803>>> f.clean({}, '') 
    779804Traceback (most recent call last): 
    780805... 
    781 ValidationError: [u'No file was submitted.'] 
     806ValidationError: [u'This field is required.'] 
    782807 
    783808>>> f.clean({}, 'files/test3.pdf') 
    784809'files/test3.pdf' 
     
    786811>>> f.clean('some content that is not a file') 
    787812Traceback (most recent call last): 
    788813... 
    789 ValidationError: [u'No file was submitted. Check the encoding type on the form.'] 
     814TypeCoercionError: [u'No file was submitted. Check the encoding type on the form.'] 
    790815 
    791816>>> f.clean({'filename': 'name', 'content': None}) 
    792817Traceback (most recent call last): 
     
    9931018 
    9941019>>> f = ChoiceField(choices=[('1', '1'), ('2', '2')], required=False) 
    9951020>>> f.clean('') 
    996 u'' 
    9971021>>> f.clean(None) 
    998 u'' 
    9991022>>> f.clean(1) 
    10001023u'1' 
    10011024>>> f.clean('1') 
     
    10511074>>> f.clean('hello') 
    10521075Traceback (most recent call last): 
    10531076... 
    1054 ValidationError: [u'Enter a list of values.'] 
     1077TypeCoercionError: [u'Enter a list of values.'] 
    10551078>>> f.clean([]) 
    10561079Traceback (most recent call last): 
    10571080... 
     
    10831106>>> f.clean('hello') 
    10841107Traceback (most recent call last): 
    10851108... 
    1086 ValidationError: [u'Enter a list of values.'] 
     1109TypeCoercionError: [u'Enter a list of values.'] 
    10871110>>> f.clean([]) 
    10881111[] 
    10891112>>> f.clean(()) 
     
    11861209>>> f.clean('hello') 
    11871210Traceback (most recent call last): 
    11881211... 
    1189 ValidationError: [u'Enter a list of values.'] 
     1212TypeCoercionError: [u'Enter a list of values.'] 
    11901213>>> f.clean(['hello', 'there']) 
    11911214Traceback (most recent call last): 
    11921215... 
     
    12121235>>> f.clean('hello') 
    12131236Traceback (most recent call last): 
    12141237... 
    1215 ValidationError: [u'Enter a list of values.'] 
     1238TypeCoercionError: [u'Enter a list of values.'] 
    12161239>>> f.clean(['hello', 'there']) 
    12171240Traceback (most recent call last): 
    12181241... 
  • a/tests/regressiontests/forms/localflavor/br.py

    old new  
    8585... 
    8686ValidationError: [u'Invalid CNPJ number.'] 
    8787>>> f.clean('64.132.916/0001-88') 
    88 '64.132.916/0001-88' 
     88u'64.132.916/0001-88' 
    8989>>> f.clean('64-132-916/0001-88') 
    90 '64-132-916/0001-88' 
     90u'64-132-916/0001-88' 
    9191>>> f.clean('64132916/0001-88') 
    92 '64132916/0001-88' 
     92u'64132916/0001-88' 
    9393>>> f.clean('64.132.916/0001-XX') 
    9494Traceback (most recent call last): 
    9595... 
  • a/tests/regressiontests/forms/localflavor/generic.py

    old new  
    3838>>> f.clean('2006-4-31') 
    3939Traceback (most recent call last): 
    4040... 
    41 ValidationError: [u'Enter a valid date.'] 
     41TypeCoercionError: [u'Enter a valid date.'] 
    4242>>> f.clean('200a-10-25') 
    4343Traceback (most recent call last): 
    4444... 
    45 ValidationError: [u'Enter a valid date.'] 
     45TypeCoercionError: [u'Enter a valid date.'] 
    4646>>> f.clean('10/25/06') 
    4747Traceback (most recent call last): 
    4848... 
    49 ValidationError: [u'Enter a valid date.'] 
     49TypeCoercionError: [u'Enter a valid date.'] 
    5050>>> f.clean(None) 
    5151Traceback (most recent call last): 
    5252... 
     
    7474>>> f.clean('2006-10-25') 
    7575Traceback (most recent call last): 
    7676... 
    77 ValidationError: [u'Enter a valid date.'] 
     77TypeCoercionError: [u'Enter a valid date.'] 
    7878>>> f.clean('25/10/2006') 
    7979Traceback (most recent call last): 
    8080... 
    81 ValidationError: [u'Enter a valid date.'] 
     81TypeCoercionError: [u'Enter a valid date.'] 
    8282>>> f.clean('25/10/06') 
    8383Traceback (most recent call last): 
    8484... 
    85 ValidationError: [u'Enter a valid date.'] 
     85TypeCoercionError: [u'Enter a valid date.'] 
    8686 
    8787## Generic DateTimeField ###################################################### 
    8888 
     
    126126>>> f.clean('hello') 
    127127Traceback (most recent call last): 
    128128... 
    129 ValidationError: [u'Enter a valid date/time.'] 
     129TypeCoercionError: [u'Enter a valid date/time.'] 
    130130>>> f.clean('2006-10-25 4:30 p.m.') 
    131131Traceback (most recent call last): 
    132132... 
    133 ValidationError: [u'Enter a valid date/time.'] 
     133TypeCoercionError: [u'Enter a valid date/time.'] 
    134134 
    135135DateField accepts an optional input_formats parameter: 
    136136>>> f = DateTimeField(input_formats=['%Y %m %d %I:%M %p']) 
     
    150150>>> f.clean('2006-10-25 14:30:45') 
    151151Traceback (most recent call last): 
    152152... 
    153 ValidationError: [u'Enter a valid date/time.'] 
     153TypeCoercionError: [u'Enter a valid date/time.'] 
    154154 
    155155>>> f = DateTimeField(required=False) 
    156156>>> f.clean(None) 
  • a/tests/regressiontests/forms/util.py

    old new  
    55 
    66tests = r""" 
    77>>> from django.newforms.util import * 
    8 >>> from django.utils.translation import ugettext_lazy 
    98 
    109########### 
    1110# flatatt # 
     
    1817u' class="news" title="Read this"' 
    1918>>> flatatt({}) 
    2019u'' 
    21  
    22 ################### 
    23 # ValidationError # 
    24 ################### 
    25  
    26 # Can take a string. 
    27 >>> print ValidationError("There was an error.").messages 
    28 <ul class="errorlist"><li>There was an error.</li></ul> 
    29  
    30 # Can take a unicode string. 
    31 >>> print ValidationError(u"Not \u03C0.").messages 
    32 <ul class="errorlist"><li>Not π.</li></ul> 
    33  
    34 # Can take a lazy string. 
    35 >>> print ValidationError(ugettext_lazy("Error.")).messages 
    36 <ul class="errorlist"><li>Error.</li></ul> 
    37  
    38 # Can take a list. 
    39 >>> print ValidationError(["Error one.", "Error two."]).messages 
    40 <ul class="errorlist"><li>Error one.</li><li>Error two.</li></ul> 
    41  
    42 # Can take a mixture in a list. 
    43 >>> print ValidationError(["First error.", u"Not \u03C0.", ugettext_lazy("Error.")]).messages 
    44 <ul class="errorlist"><li>First error.</li><li>Not π.</li><li>Error.</li></ul> 
    45  
    46 >>> class VeryBadError: 
    47 ...     def __unicode__(self): return u"A very bad error." 
    48  
    49 # Can take a non-string. 
    50 >>> print ValidationError(VeryBadError()).messages 
    51 <ul class="errorlist"><li>A very bad error.</li></ul> 
    5220"""