Django

Code

Ticket #3457: newforms_error_messages.patch

File newforms_error_messages.patch, 22.8 kB (added by SmileyChris, 1 year ago)
  • django/newforms/fields.py

    old new  
    66import re 
    77import time 
    88 
    9 from django.utils.translation import ugettext 
     9from django.utils.translation import ugettext_lazy 
    1010from django.utils.encoding import StrAndUnicode, smart_unicode 
    1111 
    1212from util import ErrorList, ValidationError 
     
    4444class Field(object): 
    4545    widget = TextInput # Default widget to use when rendering this type of Field. 
    4646    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    } 
    4751 
    4852    # Tracks each time a Field instance is created. Used to retain order. 
    4953    creation_counter = 0 
    5054 
    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): 
    5257        # required -- Boolean that specifies whether the field is required. 
    5358        #             True by default. 
    5459        # widget -- A Widget class, or instance of a Widget class, that should 
     
    8186        self.creation_counter = Field.creation_counter 
    8287        Field.creation_counter += 1 
    8388 
     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 
    84103    def clean(self, value): 
    85104        """ 
    86105        Validates the given value and returns its "cleaned" value as an 
     
    89108        Raises ValidationError for any errors. 
    90109        """ 
    91110        if self.required and value in EMPTY_VALUES: 
    92             raise ValidationError(ugettext(u'This field is required.')
     111            raise ValidationError(self.error_messages['required']
    93112        return value 
    94113 
    95114    def widget_attrs(self, widget): 
     
    101120        return {} 
    102121 
    103122class 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 
    104128    def __init__(self, max_length=None, min_length=None, *args, **kwargs): 
    105129        self.max_length, self.min_length = max_length, min_length 
    106130        super(CharField, self).__init__(*args, **kwargs) 
     
    113137        value = smart_unicode(value) 
    114138        value_length = len(value) 
    115139        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}) 
    117141        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}) 
    119143        return value 
    120144 
    121145    def widget_attrs(self, widget): 
     
    124148            return {'maxlength': str(self.max_length)} 
    125149 
    126150class 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 
    127157    def __init__(self, max_value=None, min_value=None, *args, **kwargs): 
    128158        self.max_value, self.min_value = max_value, min_value 
    129159        super(IntegerField, self).__init__(*args, **kwargs) 
     
    139169        try: 
    140170            value = int(value) 
    141171        except (ValueError, TypeError): 
    142             raise ValidationError(ugettext(u'Enter a whole number.')
     172            raise ValidationError(self.error_messages['invalid']
    143173        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) 
    145175        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) 
    147177        return value 
    148178 
    149179class 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 
    150186    def __init__(self, max_value=None, min_value=None, *args, **kwargs): 
    151187        self.max_value, self.min_value = max_value, min_value 
    152188        Field.__init__(self, *args, **kwargs) 
     
    162198        try: 
    163199            value = float(value) 
    164200        except (ValueError, TypeError): 
    165             raise ValidationError(ugettext('Enter a number.')
     201            raise ValidationError(self.error_messages['invalid']
    166202        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) 
    168204        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) 
    170206        return value 
    171207 
    172208class 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 
    173218    def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs): 
    174219        self.max_value, self.min_value = max_value, min_value 
    175220        self.max_digits, self.decimal_places = max_digits, decimal_places 
     
    189234        try: 
    190235            value = Decimal(value) 
    191236        except DecimalException: 
    192             raise ValidationError(ugettext('Enter a number.')
     237            raise ValidationError(self.error_messages['invalid']
    193238        pieces = str(value).lstrip("-").split('.') 
    194239        decimals = (len(pieces) == 2) and len(pieces[1]) or 0 
    195240        digits = len(pieces[0]) 
    196241        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) 
    198243        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) 
    200245        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) 
    202247        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) 
    204249        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)) 
    206251        return value 
    207252 
    208253DEFAULT_DATE_INPUT_FORMATS = ( 
     
    214259) 
    215260 
    216261class DateField(Field): 
     262    default_error_messages = { 
     263        'invalid': ugettext_lazy(u'Enter a valid date.'), 
     264    } 
     265 
    217266    def __init__(self, input_formats=None, *args, **kwargs): 
    218267        super(DateField, self).__init__(*args, **kwargs) 
    219268        self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS 
     
    235284                return datetime.date(*time.strptime(value, format)[:3]) 
    236285            except ValueError: 
    237286                continue 
    238         raise ValidationError(ugettext(u'Enter a valid date.')
     287        raise ValidationError(self.error_messages['invalid']
    239288 
    240289DEFAULT_TIME_INPUT_FORMATS = ( 
    241290    '%H:%M:%S',     # '14:30:59' 
     
    243292) 
    244293 
    245294class TimeField(Field): 
     295    default_error_messages = { 
     296        'invalid': ugettext_lazy(u'Enter a valid time.') 
     297    } 
     298 
    246299    def __init__(self, input_formats=None, *args, **kwargs): 
    247300        super(TimeField, self).__init__(*args, **kwargs) 
    248301        self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS 
     
    262315                return datetime.time(*time.strptime(value, format)[3:6]) 
    263316            except ValueError: 
    264317                continue 
    265         raise ValidationError(ugettext(u'Enter a valid time.')
     318        raise ValidationError(self.error_messages['invalid']
    266319 
    267320DEFAULT_DATETIME_INPUT_FORMATS = ( 
    268321    '%Y-%m-%d %H:%M:%S',     # '2006-10-25 14:30:59' 
     
    277330) 
    278331 
    279332class DateTimeField(Field): 
     333    default_error_messages = { 
     334        'invalid': ugettext_lazy(u'Enter a valid date/time.'), 
     335    } 
     336 
    280337    def __init__(self, input_formats=None, *args, **kwargs): 
    281338        super(DateTimeField, self).__init__(*args, **kwargs) 
    282339        self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS 
     
    298355                return datetime.datetime(*time.strptime(value, format)[:6]) 
    299356            except ValueError: 
    300357                continue 
    301         raise ValidationError(ugettext(u'Enter a valid date/time.')
     358        raise ValidationError(self.error_messages['invalid']
    302359 
    303360class RegexField(CharField): 
    304361    def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs): 
     
    307364        error_message is an optional error message to use, if 
    308365        'Enter a valid value' is too generic for you. 
    309366        """ 
     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 
    310372        super(RegexField, self).__init__(max_length, min_length, *args, **kwargs) 
    311373        if isinstance(regex, basestring): 
    312374            regex = re.compile(regex) 
    313375        self.regex = regex 
    314         self.error_message = error_message or ugettext(u'Enter a valid value.') 
    315376 
    316377    def clean(self, value): 
    317378        """ 
     
    322383        if value == u'': 
    323384            return value 
    324385        if not self.regex.search(value): 
    325             raise ValidationError(self.error_message
     386            raise ValidationError(self.error_messages['invalid']
    326387        return value 
    327388 
    328389email_re = re.compile( 
     
    331392    r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE)  # domain 
    332393 
    333394class 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    } 
    337398 
    338399url_re = re.compile( 
    339400    r'^https?://' # http:// or https:// 
     
    363424 
    364425class FileField(Field): 
    365426    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 
    366433    def __init__(self, *args, **kwargs): 
    367434        super(FileField, self).__init__(*args, **kwargs) 
    368435 
     
    373440        try: 
    374441            f = UploadedFile(data['filename'], data['content']) 
    375442        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']
    377444        except KeyError: 
    378             raise ValidationError(ugettext(u"No file was submitted.")
     445            raise ValidationError(self.error_messages['missing']
    379446        if not f.content: 
    380             raise ValidationError(ugettext(u"The submitted file is empty.")
     447            raise ValidationError(self.error_messages['empty']
    381448        return f 
    382449 
    383450class 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 
    384455    def clean(self, data): 
    385456        """ 
    386457        Checks that the file-upload field data contains a valid image (GIF, JPG, 
     
    394465        try: 
    395466            Image.open(StringIO(f.content)) 
    396467        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']
    398469        return f 
    399470 
    400471class 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 
    401477    def __init__(self, max_length=None, min_length=None, verify_exists=False, 
    402478            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) 
    404480        self.verify_exists = verify_exists 
    405481        self.user_agent = validator_user_agent 
    406482 
     
    422498                req = urllib2.Request(value, None, headers) 
    423499                u = urllib2.urlopen(req) 
    424500            except ValueError: 
    425                 raise ValidationError(ugettext(u'Enter a valid URL.')
     501                raise ValidationError(self.error_messages['invalid']
    426502            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']
    428504        return value 
    429505 
    430506class BooleanField(Field): 
     
    447523 
    448524class ChoiceField(Field): 
    449525    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    } 
    450529 
    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) 
    453534        self.choices = choices 
    454535 
    455536    def _get_choices(self): 
     
    475556            return value 
    476557        valid_values = set([smart_unicode(k) for k, v in self.choices]) 
    477558        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}
    479560        return value 
    480561 
    481562class MultipleChoiceField(ChoiceField): 
    482563    hidden_widget = MultipleHiddenInput 
    483564    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    } 
    484568 
    485569    def clean(self, value): 
    486570        """ 
    487571        Validates that the input is a list or tuple. 
    488572        """ 
    489573        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.')) 
    491575        elif not self.required and not value: 
    492576            return [] 
    493577        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.')) 
    495579        new_value = [smart_unicode(val) for val in value] 
    496580        # Validate that each value in the value list is in self.choices. 
    497581        valid_values = set([smart_unicode(k) for k, v in self.choices]) 
    498582        for val in new_value: 
    499583            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}
    501585        return new_value 
    502586 
    503587class ComboField(Field): 
     
    540624 
    541625    You'll probably want to use this with MultiWidget. 
    542626    """ 
     627    default_error_messages = { 
     628        'invalid': ugettext_lazy(u'Enter a list of values.'), 
     629    } 
    543630    def __init__(self, fields=(), *args, **kwargs): 
    544631        super(MultiValueField, self).__init__(*args, **kwargs) 
    545632        # Set 'required' to False on the individual fields, because the 
     
    563650        if not value or isinstance(value, (list, tuple)): 
    564651            if not value or not [v for v in value if v not in EMPTY_VALUES]: 
    565652                if self.required: 
    566                     raise ValidationError(ugettext(u'This field is required.')
     653                    raise ValidationError(self.error_messages['required']
    567654                else: 
    568655                    return self.compress([]) 
    569656        else: 
    570             raise ValidationError(ugettext(u'Enter a list of values.')
     657            raise ValidationError(self.error_messages['invalid']
    571658        for i, field in enumerate(self.fields): 
    572659            try: 
    573660                field_value = value[i] 
    574661            except IndexError: 
    575662                field_value = None 
    576663            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']
    578665            try: 
    579666                clean_data.append(field.clean(field_value)) 
    580667            except ValidationError, e: 
     
    598685        raise NotImplementedError('Subclasses must implement this method.') 
    599686 
    600687class 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    } 
    601692    def __init__(self, *args, **kwargs): 
    602693        fields = (DateField(), TimeField()) 
    603694        super(SplitDateTimeField, self).__init__(fields, *args, **kwargs) 
     
    607698            # Raise a validation error if time or date is empty 
    608699            # (possible if SplitDateTimeField has required=False). 
    609700            if data_list[0] in EMPTY_VALUES: 
    610                 raise ValidationError(ugettext(u'Enter a valid date.')
     701                raise ValidationError(self.error_messages['invalid_date']
    611702            if data_list[1] in EMPTY_VALUES: 
    612                 raise ValidationError(ugettext(u'Enter a valid time.')
     703                raise ValidationError(self.error_messages['invalid_time']
    613704            return datetime.datetime.combine(*data_list) 
    614705        return None 
  • django/newforms/util.py

    old new  
    11from django.utils.html import escape 
    2 from django.utils.encoding import smart_unicode, StrAndUnicode 
     2from django.utils.encoding import smart_unicode, force_unicode, StrAndUnicode 
    33 
    44def flatatt(attrs): 
    55    """ 
     
    4747        if isinstance(message, list): 
    4848            self.messages = ErrorList([smart_unicode(msg) for msg in message]) 
    4949        else: 
    50             assert isinstance(message, basestring), ("%s should be a basestring" % repr(message)) 
    5150            message = smart_unicode(message) 
    5251            self.messages = ErrorList([message]) 
    5352 
     
    5655        # instance would result in this: 
    5756        # AttributeError: ValidationError instance has no attribute 'args' 
    5857        # 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

    old new  
    88        'get_language', 'get_language_bidi', 'get_date_formats', 
    99        'get_partial_date_formats', 'check_for_language', 'to_locale', 
    1010        'get_language_from_request', 'install', 'templatize', 'ugettext', 
    11         'ungettext', 'deactivate_all'] 
     11        'ugettext_lazy', 'ungettext', 'deactivate_all'] 
    1212 
    1313# Here be dragons, so a short explanation of the logic won't hurt: 
    1414# We are trying to solve two problems: (1) access settings, in particular