Ticket #3457: newforms_error_messages.patch
File newforms_error_messages.patch, 22.8 KB (added by , 17 years ago) |
---|
-
django/newforms/fields.py
6 6 import re 7 7 import time 8 8 9 from django.utils.translation import ugettext 9 from django.utils.translation import ugettext_lazy 10 10 from django.utils.encoding import StrAndUnicode, smart_unicode 11 11 12 12 from util import ErrorList, ValidationError … … 44 44 class Field(object): 45 45 widget = TextInput # Default widget to use when rendering this type of Field. 46 46 hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden". 47 default_error_messages = { 48 'required': ugettext_lazy(u'This field is required.'), 49 'invalid': ugettext_lazy(u'Enter a valid value.'), 50 } 47 51 48 52 # Tracks each time a Field instance is created. Used to retain order. 49 53 creation_counter = 0 50 54 51 def __init__(self, required=True, widget=None, label=None, initial=None, help_text=None): 55 def __init__(self, required=True, widget=None, label=None, initial=None, 56 help_text=None, error_messages=None): 52 57 # required -- Boolean that specifies whether the field is required. 53 58 # True by default. 54 59 # widget -- A Widget class, or instance of a Widget class, that should … … 81 86 self.creation_counter = Field.creation_counter 82 87 Field.creation_counter += 1 83 88 89 self.error_messages = self._build_error_messages(error_messages) 90 91 def _build_error_messages(self, extra_error_messages): 92 error_messages = {} 93 def get_default_error_messages(klass): 94 for base_class in klass.__bases__: 95 get_default_error_messages(base_class) 96 if hasattr(klass, 'default_error_messages'): 97 error_messages.update(klass.default_error_messages) 98 get_default_error_messages(self.__class__) 99 if extra_error_messages: 100 error_messages.update(extra_error_messages) 101 return error_messages 102 84 103 def clean(self, value): 85 104 """ 86 105 Validates the given value and returns its "cleaned" value as an … … 89 108 Raises ValidationError for any errors. 90 109 """ 91 110 if self.required and value in EMPTY_VALUES: 92 raise ValidationError( ugettext(u'This field is required.'))111 raise ValidationError(self.error_messages['required']) 93 112 return value 94 113 95 114 def widget_attrs(self, widget): … … 101 120 return {} 102 121 103 122 class CharField(Field): 123 default_error_messages = { 124 'max_length': ugettext_lazy(u'Ensure this value has at most %(max)d characters (it has %(length)d).'), 125 'min_length': ugettext_lazy(u'Ensure this value has at least %(min)d characters (it has %(length)d).'), 126 } 127 104 128 def __init__(self, max_length=None, min_length=None, *args, **kwargs): 105 129 self.max_length, self.min_length = max_length, min_length 106 130 super(CharField, self).__init__(*args, **kwargs) … … 113 137 value = smart_unicode(value) 114 138 value_length = len(value) 115 139 if self.max_length is not None and value_length > self.max_length: 116 raise ValidationError( ugettext(u'Ensure this value has at most %(max)d characters (it has %(length)d).')% {'max': self.max_length, 'length': value_length})140 raise ValidationError(self.error_messages['max_length'] % {'max': self.max_length, 'length': value_length}) 117 141 if self.min_length is not None and value_length < self.min_length: 118 raise ValidationError( ugettext(u'Ensure this value has at least %(min)d characters (it has %(length)d).')% {'min': self.min_length, 'length': value_length})142 raise ValidationError(self.error_messages['min_length'] % {'min': self.min_length, 'length': value_length}) 119 143 return value 120 144 121 145 def widget_attrs(self, widget): … … 124 148 return {'maxlength': str(self.max_length)} 125 149 126 150 class IntegerField(Field): 151 default_error_messages = { 152 'invalid': ugettext_lazy(u'Enter a whole number.'), 153 'max_value': ugettext_lazy(u'Ensure this value is less than or equal to %s.'), 154 'min_value': ugettext_lazy(u'Ensure this value is greater than or equal to %s.'), 155 } 156 127 157 def __init__(self, max_value=None, min_value=None, *args, **kwargs): 128 158 self.max_value, self.min_value = max_value, min_value 129 159 super(IntegerField, self).__init__(*args, **kwargs) … … 139 169 try: 140 170 value = int(value) 141 171 except (ValueError, TypeError): 142 raise ValidationError( ugettext(u'Enter a whole number.'))172 raise ValidationError(self.error_messages['invalid']) 143 173 if self.max_value is not None and value > self.max_value: 144 raise ValidationError( ugettext(u'Ensure this value is less than or equal to %s.')% self.max_value)174 raise ValidationError(self.error_messages['max_value'] % self.max_value) 145 175 if self.min_value is not None and value < self.min_value: 146 raise ValidationError( ugettext(u'Ensure this value is greater than or equal to %s.')% self.min_value)176 raise ValidationError(self.error_messages['min_value'] % self.min_value) 147 177 return value 148 178 149 179 class FloatField(Field): 180 default_error_messages = { 181 'invalid': ugettext_lazy(u'Enter a number.'), 182 'max_value': ugettext_lazy(u'Ensure this value is less than or equal to %s.'), 183 'min_value': ugettext_lazy(u'Ensure this value is greater than or equal to %s.'), 184 } 185 150 186 def __init__(self, max_value=None, min_value=None, *args, **kwargs): 151 187 self.max_value, self.min_value = max_value, min_value 152 188 Field.__init__(self, *args, **kwargs) … … 162 198 try: 163 199 value = float(value) 164 200 except (ValueError, TypeError): 165 raise ValidationError( ugettext('Enter a number.'))201 raise ValidationError(self.error_messages['invalid']) 166 202 if self.max_value is not None and value > self.max_value: 167 raise ValidationError( ugettext('Ensure this value is less than or equal to %s.')% self.max_value)203 raise ValidationError(self.error_messages['max_value'] % self.max_value) 168 204 if self.min_value is not None and value < self.min_value: 169 raise ValidationError( ugettext('Ensure this value is greater than or equal to %s.')% self.min_value)205 raise ValidationError(self.error_messages['min_value'] % self.min_value) 170 206 return value 171 207 172 208 class DecimalField(Field): 209 default_error_messages = { 210 'invalid': ugettext_lazy(u'Enter a number.'), 211 'max_value': ugettext_lazy(u'Ensure this value is less than or equal to %s.'), 212 'min_value': ugettext_lazy(u'Ensure this value is greater than or equal to %s.'), 213 'max_digits': ugettext_lazy('Ensure that there are no more than %s digits in total.'), 214 'max_decimal_places': ugettext_lazy('Ensure that there are no more than %s decimal places.'), 215 'max_whole_digits': ugettext_lazy('Ensure that there are no more than %s digits before the decimal point.') 216 } 217 173 218 def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs): 174 219 self.max_value, self.min_value = max_value, min_value 175 220 self.max_digits, self.decimal_places = max_digits, decimal_places … … 189 234 try: 190 235 value = Decimal(value) 191 236 except DecimalException: 192 raise ValidationError( ugettext('Enter a number.'))237 raise ValidationError(self.error_messages['invalid']) 193 238 pieces = str(value).lstrip("-").split('.') 194 239 decimals = (len(pieces) == 2) and len(pieces[1]) or 0 195 240 digits = len(pieces[0]) 196 241 if self.max_value is not None and value > self.max_value: 197 raise ValidationError( ugettext('Ensure this value is less than or equal to %s.')% self.max_value)242 raise ValidationError(self.error_messages['max_value'] % self.max_value) 198 243 if self.min_value is not None and value < self.min_value: 199 raise ValidationError( ugettext('Ensure this value is greater than or equal to %s.')% self.min_value)244 raise ValidationError(self.error_messages['min_value'] % self.min_value) 200 245 if self.max_digits is not None and (digits + decimals) > self.max_digits: 201 raise ValidationError( ugettext('Ensure that there are no more than %s digits in total.')% self.max_digits)246 raise ValidationError(self.error_messages['max_digits'] % self.max_digits) 202 247 if self.decimal_places is not None and decimals > self.decimal_places: 203 raise ValidationError( ugettext('Ensure that there are no more than %s decimal places.')% self.decimal_places)248 raise ValidationError(self.error_messages['max_decimal_places'] % self.decimal_places) 204 249 if self.max_digits is not None and self.decimal_places is not None and digits > (self.max_digits - self.decimal_places): 205 raise ValidationError( ugettext('Ensure that there are no more than %s digits before the decimal point.')% (self.max_digits - self.decimal_places))250 raise ValidationError(self.error_messages['max_whole_digits'] % (self.max_digits - self.decimal_places)) 206 251 return value 207 252 208 253 DEFAULT_DATE_INPUT_FORMATS = ( … … 214 259 ) 215 260 216 261 class DateField(Field): 262 default_error_messages = { 263 'invalid': ugettext_lazy(u'Enter a valid date.'), 264 } 265 217 266 def __init__(self, input_formats=None, *args, **kwargs): 218 267 super(DateField, self).__init__(*args, **kwargs) 219 268 self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS … … 235 284 return datetime.date(*time.strptime(value, format)[:3]) 236 285 except ValueError: 237 286 continue 238 raise ValidationError( ugettext(u'Enter a valid date.'))287 raise ValidationError(self.error_messages['invalid']) 239 288 240 289 DEFAULT_TIME_INPUT_FORMATS = ( 241 290 '%H:%M:%S', # '14:30:59' … … 243 292 ) 244 293 245 294 class TimeField(Field): 295 default_error_messages = { 296 'invalid': ugettext_lazy(u'Enter a valid time.') 297 } 298 246 299 def __init__(self, input_formats=None, *args, **kwargs): 247 300 super(TimeField, self).__init__(*args, **kwargs) 248 301 self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS … … 262 315 return datetime.time(*time.strptime(value, format)[3:6]) 263 316 except ValueError: 264 317 continue 265 raise ValidationError( ugettext(u'Enter a valid time.'))318 raise ValidationError(self.error_messages['invalid']) 266 319 267 320 DEFAULT_DATETIME_INPUT_FORMATS = ( 268 321 '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59' … … 277 330 ) 278 331 279 332 class DateTimeField(Field): 333 default_error_messages = { 334 'invalid': ugettext_lazy(u'Enter a valid date/time.'), 335 } 336 280 337 def __init__(self, input_formats=None, *args, **kwargs): 281 338 super(DateTimeField, self).__init__(*args, **kwargs) 282 339 self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS … … 298 355 return datetime.datetime(*time.strptime(value, format)[:6]) 299 356 except ValueError: 300 357 continue 301 raise ValidationError( ugettext(u'Enter a valid date/time.'))358 raise ValidationError(self.error_messages['invalid']) 302 359 303 360 class RegexField(CharField): 304 361 def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs): … … 307 364 error_message is an optional error message to use, if 308 365 'Enter a valid value' is too generic for you. 309 366 """ 367 # error_message is just kept for backwards compatibility: 368 if error_message: 369 error_messages = kwargs.get('error_messages') or {} 370 error_messages['invalid'] = error_message 371 kwargs['error_messages'] = error_messages 310 372 super(RegexField, self).__init__(max_length, min_length, *args, **kwargs) 311 373 if isinstance(regex, basestring): 312 374 regex = re.compile(regex) 313 375 self.regex = regex 314 self.error_message = error_message or ugettext(u'Enter a valid value.')315 376 316 377 def clean(self, value): 317 378 """ … … 322 383 if value == u'': 323 384 return value 324 385 if not self.regex.search(value): 325 raise ValidationError(self.error_message )386 raise ValidationError(self.error_messages['invalid']) 326 387 return value 327 388 328 389 email_re = re.compile( … … 331 392 r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain 332 393 333 394 class EmailField(RegexField): 334 def __init__(self, max_length=None, min_length=None, *args, **kwargs):335 RegexField.__init__(self, email_re, max_length, min_length,336 ugettext(u'Enter a valid e-mail address.'), *args, **kwargs)395 default_error_messages = { 396 'invalid': ugettext_lazy(u'Enter a valid e-mail address.'), 397 } 337 398 338 399 url_re = re.compile( 339 400 r'^https?://' # http:// or https:// … … 363 424 364 425 class FileField(Field): 365 426 widget = FileInput 427 default_error_messages = { 428 'invalid': ugettext_lazy(u"No file was submitted. Check the encoding type on the form."), 429 'missing': ugettext_lazy(u"No file was submitted."), 430 'empty': ugettext_lazy(u"The submitted file is empty."), 431 } 432 366 433 def __init__(self, *args, **kwargs): 367 434 super(FileField, self).__init__(*args, **kwargs) 368 435 … … 373 440 try: 374 441 f = UploadedFile(data['filename'], data['content']) 375 442 except TypeError: 376 raise ValidationError( ugettext(u"No file was submitted. Check the encoding type on the form."))443 raise ValidationError(self.error_messages['invalid']) 377 444 except KeyError: 378 raise ValidationError( ugettext(u"No file was submitted."))445 raise ValidationError(self.error_messages['missing']) 379 446 if not f.content: 380 raise ValidationError( ugettext(u"The submitted file is empty."))447 raise ValidationError(self.error_messages['empty']) 381 448 return f 382 449 383 450 class ImageField(FileField): 451 default_error_messages = { 452 'invalid_image': ugettext_lazy(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image."), 453 } 454 384 455 def clean(self, data): 385 456 """ 386 457 Checks that the file-upload field data contains a valid image (GIF, JPG, … … 394 465 try: 395 466 Image.open(StringIO(f.content)) 396 467 except IOError: # Python Imaging Library doesn't recognize it as an image 397 raise ValidationError( ugettext(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image."))468 raise ValidationError(self.error_messages['invalid_image']) 398 469 return f 399 470 400 471 class URLField(RegexField): 472 default_error_messages = { 473 'invalid': ugettext_lazy(u'Enter a valid URL.'), 474 'invalid_link': ugettext_lazy(u'This URL appears to be a broken link.'), 475 } 476 401 477 def __init__(self, max_length=None, min_length=None, verify_exists=False, 402 478 validator_user_agent=URL_VALIDATOR_USER_AGENT, *args, **kwargs): 403 super(URLField, self).__init__(url_re, max_length, min_length, ugettext (u'Enter a valid URL.'), *args, **kwargs)479 super(URLField, self).__init__(url_re, max_length, min_length, ugettext_lazy(u'Enter a valid URL.'), *args, **kwargs) 404 480 self.verify_exists = verify_exists 405 481 self.user_agent = validator_user_agent 406 482 … … 422 498 req = urllib2.Request(value, None, headers) 423 499 u = urllib2.urlopen(req) 424 500 except ValueError: 425 raise ValidationError( ugettext(u'Enter a valid URL.'))501 raise ValidationError(self.error_messages['invalid']) 426 502 except: # urllib2.URLError, httplib.InvalidURL, etc. 427 raise ValidationError( ugettext(u'This URL appears to be a broken link.'))503 raise ValidationError(self.error_messages['invalid_link']) 428 504 return value 429 505 430 506 class BooleanField(Field): … … 447 523 448 524 class ChoiceField(Field): 449 525 widget = Select 526 default_error_messages = { 527 'invalid_choice': ugettext_lazy(u'Select a valid choice. That choice is not one of the available choices.'), 528 } 450 529 451 def __init__(self, choices=(), required=True, widget=None, label=None, initial=None, help_text=None): 452 super(ChoiceField, self).__init__(required, widget, label, initial, help_text) 530 def __init__(self, choices=(), required=True, widget=None, label=None, 531 initial=None, help_text=None, *args, **kwargs): 532 super(ChoiceField, self).__init__(required, widget, label, initial, 533 help_text, *args, **kwargs) 453 534 self.choices = choices 454 535 455 536 def _get_choices(self): … … 475 556 return value 476 557 valid_values = set([smart_unicode(k) for k, v in self.choices]) 477 558 if value not in valid_values: 478 raise ValidationError( ugettext(u'Select a valid choice. That choice is not one of the available choices.'))559 raise ValidationError(self.error_messages['invalid_choice'] % {'value': value}) 479 560 return value 480 561 481 562 class MultipleChoiceField(ChoiceField): 482 563 hidden_widget = MultipleHiddenInput 483 564 widget = SelectMultiple 565 default_error_messages = { 566 'invalid_choice': ugettext_lazy(u'Select a valid choice. %(value)s is not one of the available choices.') 567 } 484 568 485 569 def clean(self, value): 486 570 """ 487 571 Validates that the input is a list or tuple. 488 572 """ 489 573 if self.required and not value: 490 raise ValidationError(ugettext (u'This field is required.'))574 raise ValidationError(ugettext_lazy(u'This field is required.')) 491 575 elif not self.required and not value: 492 576 return [] 493 577 if not isinstance(value, (list, tuple)): 494 raise ValidationError(ugettext (u'Enter a list of values.'))578 raise ValidationError(ugettext_lazy(u'Enter a list of values.')) 495 579 new_value = [smart_unicode(val) for val in value] 496 580 # Validate that each value in the value list is in self.choices. 497 581 valid_values = set([smart_unicode(k) for k, v in self.choices]) 498 582 for val in new_value: 499 583 if val not in valid_values: 500 raise ValidationError( ugettext(u'Select a valid choice. %s is not one of the available choices.') % val)584 raise ValidationError(self.error_messages['invalid_choice'] % {'value': val}) 501 585 return new_value 502 586 503 587 class ComboField(Field): … … 540 624 541 625 You'll probably want to use this with MultiWidget. 542 626 """ 627 default_error_messages = { 628 'invalid': ugettext_lazy(u'Enter a list of values.'), 629 } 543 630 def __init__(self, fields=(), *args, **kwargs): 544 631 super(MultiValueField, self).__init__(*args, **kwargs) 545 632 # Set 'required' to False on the individual fields, because the … … 563 650 if not value or isinstance(value, (list, tuple)): 564 651 if not value or not [v for v in value if v not in EMPTY_VALUES]: 565 652 if self.required: 566 raise ValidationError( ugettext(u'This field is required.'))653 raise ValidationError(self.error_messages['required']) 567 654 else: 568 655 return self.compress([]) 569 656 else: 570 raise ValidationError( ugettext(u'Enter a list of values.'))657 raise ValidationError(self.error_messages['invalid']) 571 658 for i, field in enumerate(self.fields): 572 659 try: 573 660 field_value = value[i] 574 661 except IndexError: 575 662 field_value = None 576 663 if self.required and field_value in EMPTY_VALUES: 577 raise ValidationError( ugettext(u'This field is required.'))664 raise ValidationError(self.error_messages['required']) 578 665 try: 579 666 clean_data.append(field.clean(field_value)) 580 667 except ValidationError, e: … … 598 685 raise NotImplementedError('Subclasses must implement this method.') 599 686 600 687 class SplitDateTimeField(MultiValueField): 688 default_error_messages = { 689 'invalid_date': ugettext_lazy(u'Enter a valid date.'), 690 'invalid_time': ugettext_lazy(u'Enter a valid time.'), 691 } 601 692 def __init__(self, *args, **kwargs): 602 693 fields = (DateField(), TimeField()) 603 694 super(SplitDateTimeField, self).__init__(fields, *args, **kwargs) … … 607 698 # Raise a validation error if time or date is empty 608 699 # (possible if SplitDateTimeField has required=False). 609 700 if data_list[0] in EMPTY_VALUES: 610 raise ValidationError( ugettext(u'Enter a valid date.'))701 raise ValidationError(self.error_messages['invalid_date']) 611 702 if data_list[1] in EMPTY_VALUES: 612 raise ValidationError( ugettext(u'Enter a valid time.'))703 raise ValidationError(self.error_messages['invalid_time']) 613 704 return datetime.datetime.combine(*data_list) 614 705 return None -
django/newforms/util.py
1 1 from django.utils.html import escape 2 from django.utils.encoding import smart_unicode, StrAndUnicode2 from django.utils.encoding import smart_unicode, force_unicode, StrAndUnicode 3 3 4 4 def flatatt(attrs): 5 5 """ … … 47 47 if isinstance(message, list): 48 48 self.messages = ErrorList([smart_unicode(msg) for msg in message]) 49 49 else: 50 assert isinstance(message, basestring), ("%s should be a basestring" % repr(message))51 50 message = smart_unicode(message) 52 51 self.messages = ErrorList([message]) 53 52 … … 56 55 # instance would result in this: 57 56 # AttributeError: ValidationError instance has no attribute 'args' 58 57 # See http://www.python.org/doc/current/tut/node10.html#handling 59 return repr(self.messages) 58 # We also force any lazy messages to unicode. 59 return repr([force_unicode(message) for message in self.messages]) -
django/utils/translation/__init__.py
8 8 'get_language', 'get_language_bidi', 'get_date_formats', 9 9 'get_partial_date_formats', 'check_for_language', 'to_locale', 10 10 'get_language_from_request', 'install', 'templatize', 'ugettext', 11 'u ngettext', 'deactivate_all']11 'ugettext_lazy', 'ungettext', 'deactivate_all'] 12 12 13 13 # Here be dragons, so a short explanation of the logic won't hurt: 14 14 # We are trying to solve two problems: (1) access settings, in particular