Changeset 5302
- Timestamp:
- 05/20/07 20:29:58 (1 year ago)
- Files:
-
- django/trunk/AUTHORS (modified) (2 diffs)
- django/trunk/django/contrib/admin/templatetags/admin_list.py (modified) (1 diff)
- django/trunk/django/contrib/admin/views/doc.py (modified) (1 diff)
- django/trunk/django/core/management.py (modified) (2 diffs)
- django/trunk/django/core/serializers/json.py (modified) (5 diffs)
- django/trunk/django/core/validators.py (modified) (2 diffs)
- django/trunk/django/db/backends/ado_mssql/creation.py (modified) (1 diff)
- django/trunk/django/db/backends/mysql/base.py (modified) (2 diffs)
- django/trunk/django/db/backends/mysql/creation.py (modified) (1 diff)
- django/trunk/django/db/backends/mysql/introspection.py (modified) (1 diff)
- django/trunk/django/db/backends/mysql_old/base.py (modified) (1 diff)
- django/trunk/django/db/backends/mysql_old/creation.py (modified) (1 diff)
- django/trunk/django/db/backends/mysql_old/introspection.py (modified) (1 diff)
- django/trunk/django/db/backends/oracle/creation.py (modified) (1 diff)
- django/trunk/django/db/backends/oracle/introspection.py (modified) (1 diff)
- django/trunk/django/db/backends/postgresql/base.py (modified) (1 diff)
- django/trunk/django/db/backends/postgresql/creation.py (modified) (1 diff)
- django/trunk/django/db/backends/postgresql/introspection.py (modified) (2 diffs)
- django/trunk/django/db/backends/postgresql_psycopg2/introspection.py (modified) (2 diffs)
- django/trunk/django/db/backends/sqlite3/base.py (modified) (2 diffs)
- django/trunk/django/db/backends/sqlite3/creation.py (modified) (1 diff)
- django/trunk/django/db/backends/util.py (modified) (3 diffs)
- django/trunk/django/db/models/fields/__init__.py (modified) (3 diffs)
- django/trunk/django/newforms/fields.py (modified) (3 diffs)
- django/trunk/django/oldforms/__init__.py (modified) (2 diffs)
- django/trunk/django/utils/_decimal.py (added)
- django/trunk/docs/forms.txt (modified) (2 diffs)
- django/trunk/docs/model-api.txt (modified) (2 diffs)
- django/trunk/docs/newforms.txt (modified) (2 diffs)
- django/trunk/tests/modeltests/invalid_models/models.py (modified) (3 diffs)
- django/trunk/tests/regressiontests/forms/tests.py (modified) (2 diffs)
- django/trunk/tests/regressiontests/serializers_regress/models.py (modified) (6 diffs)
- django/trunk/tests/regressiontests/serializers_regress/tests.py (modified) (18 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/trunk/AUTHORS
r5288 r5302 42 42 answer newbie questions, and generally made Django that much better: 43 43 44 adurdin@gmail.com45 44 alang@bright-green.com 46 45 Marty Alchin <gulopine@gamemusic.org> … … 91 90 Maximillian Dornseif <md@hudora.de> 92 91 Jeremy Dunck <http://dunck.us/> 92 Andrew Durdin <adurdin@gmail.com> 93 93 Andy Dustman <farcepest@gmail.com> 94 94 Clint Ecker django/trunk/django/contrib/admin/templatetags/admin_list.py
r4596 r5302 167 167 elif isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField): 168 168 result_repr = _boolean_icon(field_val) 169 # FloatFields are special: Zero-pad the decimals.170 elif isinstance(f, models. FloatField):169 # DecimalFields are special: Zero-pad the decimals. 170 elif isinstance(f, models.DecimalField): 171 171 if field_val is not None: 172 172 result_repr = ('%%.%sf' % f.decimal_places) % field_val django/trunk/django/contrib/admin/views/doc.py
r4704 r5302 295 295 'DateField' : _('Date (without time)'), 296 296 'DateTimeField' : _('Date (with time)'), 297 'DecimalField' : _('Decimal number'), 297 298 'EmailField' : _('E-mail address'), 298 299 'FileField' : _('File path'), 299 300 'FilePathField' : _('File path'), 300 'FloatField' : _(' Decimalnumber'),301 'FloatField' : _('Floating point number'), 301 302 'ForeignKey' : _('Integer'), 302 303 'ImageField' : _('File path'), django/trunk/django/core/management.py
r5245 r5302 871 871 extra_params['maxlength'] = row[3] 872 872 873 if field_type == ' FloatField':873 if field_type == 'DecimalField': 874 874 extra_params['max_digits'] = row[4] 875 875 extra_params['decimal_places'] = row[5] … … 946 946 if isinstance(f, models.CharField) and f.maxlength in (None, 0): 947 947 e.add(opts, '"%s": CharFields require a "maxlength" attribute.' % f.name) 948 if isinstance(f, models. FloatField):948 if isinstance(f, models.DecimalField): 949 949 if f.decimal_places is None: 950 e.add(opts, '"%s": FloatFields require a "decimal_places" attribute.' % f.name)950 e.add(opts, '"%s": DecimalFields require a "decimal_places" attribute.' % f.name) 951 951 if f.max_digits is None: 952 e.add(opts, '"%s": FloatFields require a "max_digits" attribute.' % f.name)952 e.add(opts, '"%s": DecimalFields require a "max_digits" attribute.' % f.name) 953 953 if isinstance(f, models.FileField) and not f.upload_to: 954 954 e.add(opts, '"%s": FileFields require an "upload_to" attribute.' % f.name) django/trunk/django/core/serializers/json.py
r5075 r5302 5 5 import datetime 6 6 from django.utils import simplejson 7 from django.utils.simplejson import decoder 7 8 from django.core.serializers.python import Serializer as PythonSerializer 8 9 from django.core.serializers.python import Deserializer as PythonDeserializer … … 11 12 except ImportError: 12 13 from StringIO import StringIO 14 try: 15 import decimal 16 except ImportError: 17 from django.utils import _decimal as decimal # Python 2.3 fallback 13 18 14 19 class Serializer(PythonSerializer): … … 17 22 """ 18 23 def end_serialization(self): 19 simplejson.dump(self.objects, self.stream, cls=D ateTimeAwareJSONEncoder, **self.options)24 simplejson.dump(self.objects, self.stream, cls=DjangoJSONEncoder, **self.options) 20 25 21 26 def getvalue(self): … … 31 36 else: 32 37 stream = stream_or_string 38 #for obj in PythonDeserializer(simplejson.load(stream, cls=DjangoJSONDecoder)): 33 39 for obj in PythonDeserializer(simplejson.load(stream)): 34 40 yield obj 35 41 36 class D ateTimeAwareJSONEncoder(simplejson.JSONEncoder):42 class DjangoJSONEncoder(simplejson.JSONEncoder): 37 43 """ 38 JSONEncoder subclass that knows how to encode date/time types44 JSONEncoder subclass that knows how to encode date/time and decimal types. 39 45 """ 40 46 … … 49 55 elif isinstance(o, datetime.time): 50 56 return o.strftime(self.TIME_FORMAT) 57 elif isinstance(o, decimal.Decimal): 58 return str(o) 51 59 else: 52 return super(DateTimeAwareJSONEncoder, self).default(o) 60 return super(DjangoJSONEncoder, self).default(o) 61 62 # Older, deprecated class name (for backwards compatibility purposes). 63 DateTimeAwareJSONEncoder = DjangoJSONEncoder 64 65 ## Our override for simplejson.JSONNumber, because we want to use decimals in 66 ## preference to floats (we can convert decimal -> float when they stored, if 67 ## needed, but cannot go the other way). 68 #def DjangoNumber(match, context): 69 # match = DjangoNumber.regex.match(match.string, *match.span()) 70 # integer, frac, exp = match.groups() 71 # if exp: 72 # res = float(integer + (frac or '') + (exp or '')) 73 # elif frac: 74 # res = decimal.Decimal(integer + frac) 75 # else: 76 # res = int(integer) 77 # return res, None 78 #decoder.pattern(r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?')(DjangoNumber) 79 # 80 #converters = decoder.ANYTHING[:] 81 #converters[-1] = DjangoNumber 82 #decoder.JSONScanner = decoder.Scanner(converters) 83 # 84 #class DjangoJSONDecoder(simplejson.JSONDecoder): 85 # _scanner = decoder.Scanner(converters) 86 # django/trunk/django/core/validators.py
r5091 r5302 26 26 r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string 27 27 r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain 28 decimal_re = re.compile(r'^-?(?P<digits>\d+)(\.(?P<decimals>\d+))?$') 28 29 integer_re = re.compile(r'^-?\d+$') 29 30 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}$') … … 407 408 raise ValidationError, gettext("This value must be a power of %s.") % self.power_of 408 409 409 class IsValid Float(object):410 class IsValidDecimal(object): 410 411 def __init__(self, max_digits, decimal_places): 411 412 self.max_digits, self.decimal_places = max_digits, decimal_places 412 413 413 414 def __call__(self, field_data, all_data): 414 data = str(field_data) 415 try: 416 float(data) 417 except ValueError: 415 match = decimal_re.search(str(field_data)) 416 if not match: 418 417 raise ValidationError, gettext("Please enter a valid decimal number.") 419 # Negative floats require more space to input. 420 max_allowed_length = data.startswith('-') and (self.max_digits + 2) or (self.max_digits + 1) 421 if len(data) > max_allowed_length: 418 419 digits = len(match.group('digits') or '') 420 decimals = len(match.group('decimals') or '') 421 422 if digits + decimals > self.max_digits: 422 423 raise ValidationError, ngettext("Please enter a valid decimal number with at most %s total digit.", 423 424 "Please enter a valid decimal number with at most %s total digits.", self.max_digits) % self.max_digits 424 if (not '.' in data and len(data) > (max_allowed_length - self.decimal_places - 1)) or ('.' in data and len(data) > (max_allowed_length - (self.decimal_places - len(data.split('.')[1])))):425 if digits > (self.max_digits - self.decimal_places): 425 426 raise ValidationError, ngettext( "Please enter a valid decimal number with a whole part of at most %s digit.", 426 427 "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) 427 if '.' in data and len(data.split('.')[1])> self.decimal_places:428 if decimals > self.decimal_places: 428 429 raise ValidationError, ngettext("Please enter a valid decimal number with at most %s decimal place.", 429 430 "Please enter a valid decimal number with at most %s decimal places.", self.decimal_places) % self.decimal_places 431 432 def isValidFloat(field_data, all_data): 433 data = str(field_data) 434 try: 435 float(data) 436 except ValueError: 437 raise ValidationError, gettext("Please enter a valid floating point number.") 430 438 431 439 class HasAllowableSize(object): django/trunk/django/db/backends/ado_mssql/creation.py
r4295 r5302 6 6 'DateField': 'smalldatetime', 7 7 'DateTimeField': 'smalldatetime', 8 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 8 9 'FileField': 'varchar(100)', 9 10 'FilePathField': 'varchar(100)', 10 'FloatField': ' numeric(%(max_digits)s, %(decimal_places)s)',11 'FloatField': 'double precision', 11 12 'ImageField': 'varchar(100)', 12 13 'IntegerField': 'int', django/trunk/django/db/backends/mysql/base.py
r5076 r5302 16 16 # inadvertently passes the version test. 17 17 version = Database.version_info 18 if (version < (1,2,1) or (version[:3] == (1, 2, 1) and 18 if (version < (1,2,1) or (version[:3] == (1, 2, 1) and 19 19 (len(version) < 5 or version[3] != 'final' or version[4] < 2))): 20 20 raise ImportError, "MySQLdb-1.2.1p2 or newer is required; you have %s" % Database.__version__ … … 37 37 django_conversions.update({ 38 38 FIELD_TYPE.TIME: util.typecast_time, 39 FIELD_TYPE.DECIMAL: util.typecast_decimal, 40 FIELD_TYPE.NEWDECIMAL: util.typecast_decimal, 39 41 }) 40 42 django/trunk/django/db/backends/mysql/creation.py
r4295 r5302 10 10 'DateField': 'date', 11 11 'DateTimeField': 'datetime', 12 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 12 13 'FileField': 'varchar(100)', 13 14 'FilePathField': 'varchar(100)', 14 'FloatField': ' numeric(%(max_digits)s, %(decimal_places)s)',15 'FloatField': 'double precision', 15 16 'ImageField': 'varchar(100)', 16 17 'IntegerField': 'integer', django/trunk/django/db/backends/mysql/introspection.py
r5042 r5302 77 77 FIELD_TYPE.BLOB: 'TextField', 78 78 FIELD_TYPE.CHAR: 'CharField', 79 FIELD_TYPE.DECIMAL: ' FloatField',79 FIELD_TYPE.DECIMAL: 'DecimalField', 80 80 FIELD_TYPE.DATE: 'DateField', 81 81 FIELD_TYPE.DATETIME: 'DateTimeField', django/trunk/django/db/backends/mysql_old/base.py
r5091 r5302 25 25 FIELD_TYPE.DATE: util.typecast_date, 26 26 FIELD_TYPE.TIME: util.typecast_time, 27 FIELD_TYPE.DECIMAL: util.typecast_decimal, 28 FIELD_TYPE.NEWDECIMAL: util.typecast_decimal, 27 29 }) 28 30 django/trunk/django/db/backends/mysql_old/creation.py
r4767 r5302 10 10 'DateField': 'date', 11 11 'DateTimeField': 'datetime', 12 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 12 13 'FileField': 'varchar(100)', 13 14 'FilePathField': 'varchar(100)', 14 'FloatField': ' numeric(%(max_digits)s, %(decimal_places)s)',15 'FloatField': 'double precision', 15 16 'ImageField': 'varchar(100)', 16 17 'IntegerField': 'integer', django/trunk/django/db/backends/mysql_old/introspection.py
r4768 r5302 77 77 FIELD_TYPE.BLOB: 'TextField', 78 78 FIELD_TYPE.CHAR: 'CharField', 79 FIELD_TYPE.DECIMAL: ' FloatField',79 FIELD_TYPE.DECIMAL: 'DecimalField', 80 80 FIELD_TYPE.DATE: 'DateField', 81 81 FIELD_TYPE.DATETIME: 'DateTimeField', django/trunk/django/db/backends/oracle/creation.py
r4295 r5302 6 6 'DateField': 'date', 7 7 'DateTimeField': 'date', 8 'DecimalField': 'number(%(max_digits)s, %(decimal_places)s)', 8 9 'FileField': 'varchar2(100)', 9 10 'FilePathField': 'varchar2(100)', 10 'FloatField': ' number(%(max_digits)s, %(decimal_places)s)',11 'FloatField': 'double precision', 11 12 'ImageField': 'varchar2(100)', 12 13 'IntegerField': 'integer', django/trunk/django/db/backends/oracle/introspection.py
r4265 r5302 47 47 1184: 'DateTimeField', 48 48 1266: 'TimeField', 49 1700: ' FloatField',49 1700: 'DecimalField', 50 50 } django/trunk/django/db/backends/postgresql/base.py
r5204 r5302 250 250 Database.register_type(Database.new_type((1114,1184), "TIMESTAMP", util.typecast_timestamp)) 251 251 Database.register_type(Database.new_type((16,), "BOOLEAN", util.typecast_boolean)) 252 Database.register_type(Database.new_type((1700,), "NUMERIC", util.typecast_decimal)) 252 253 253 254 OPERATOR_MAPPING = { django/trunk/django/db/backends/postgresql/creation.py
r4295 r5302 10 10 'DateField': 'date', 11 11 'DateTimeField': 'timestamp with time zone', 12 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 12 13 'FileField': 'varchar(100)', 13 14 'FilePathField': 'varchar(100)', 14 'FloatField': ' numeric(%(max_digits)s, %(decimal_places)s)',15 'FloatField': 'double precision', 15 16 'ImageField': 'varchar(100)', 16 17 'IntegerField': 'integer', django/trunk/django/db/backends/postgresql/introspection.py
r4265 r5302 73 73 23: 'IntegerField', 74 74 25: 'TextField', 75 701: 'FloatField', 75 76 869: 'IPAddressField', 76 77 1043: 'CharField', … … 80 81 1184: 'DateTimeField', 81 82 1266: 'TimeField', 82 1700: ' FloatField',83 1700: 'DecimalField', 83 84 } django/trunk/django/db/backends/postgresql_psycopg2/introspection.py
r4265 r5302 73 73 23: 'IntegerField', 74 74 25: 'TextField', 75 701: 'FloatField', 75 76 869: 'IPAddressField', 76 77 1043: 'CharField', … … 80 81 1184: 'DateTimeField', 81 82 1266: 'TimeField', 82 1700: ' FloatField',83 1700: 'DecimalField', 83 84 } django/trunk/django/db/backends/sqlite3/base.py
r5076 r5302 18 18 raise ImproperlyConfigured, "Error loading %s module: %s" % (module, e) 19 19 20 try: 21 import decimal 22 except ImportError: 23 from django.utils import _decimal as decimal # for Python 2.3 24 20 25 DatabaseError = Database.DatabaseError 21 26 IntegrityError = Database.IntegrityError … … 27 32 Database.register_converter("timestamp", util.typecast_timestamp) 28 33 Database.register_converter("TIMESTAMP", util.typecast_timestamp) 34 Database.register_converter("decimal", util.typecast_decimal) 35 Database.register_adapter(decimal.Decimal, util.rev_typecast_decimal) 29 36 30 37 def utf8rowFactory(cursor, row): django/trunk/django/db/backends/sqlite3/creation.py
r4295 r5302 9 9 'DateField': 'date', 10 10 'DateTimeField': 'datetime', 11 'DecimalField': 'decimal', 11 12 'FileField': 'varchar(100)', 12 13 'FilePathField': 'varchar(100)', 13 'FloatField': ' numeric(%(max_digits)s, %(decimal_places)s)',14 'FloatField': 'real', 14 15 'ImageField': 'varchar(100)', 15 16 'IntegerField': 'integer', django/trunk/django/db/backends/util.py
r5091 r5302 1 1 import datetime 2 2 from time import time 3 4 try: 5 import decimal 6 except ImportError: 7 from django.utils import _decimal as decimal # for Python 2.3 3 8 4 9 class CursorDebugWrapper(object): … … 86 91 return str(s)[0].lower() == 't' 87 92 93 def typecast_decimal(s): 94 if s is None: 95 return None 96 return decimal.Decimal(s) 97 88 98 ############################################### 89 99 # Converters from Python to database (string) # … … 92 102 def rev_typecast_boolean(obj, d): 93 103 return obj and '1' or '0' 104 105 def rev_typecast_decimal(d): 106 if d is None: 107 return None 108 return str(d) 94 109 95 110 ################################################################################## django/trunk/django/db/models/fields/__init__.py
r5130 r5302 11 11 from django.utils.translation import gettext, gettext_lazy 12 12 import datetime, os, time 13 try: 14 import decimal 15 except ImportError: 16 from django.utils import _decimal as decimal # for Python 2.3 13 17 14 18 class NOT_PROVIDED: … … 573 577 defaults.update(kwargs) 574 578 return super(DateTimeField, self).formfield(**defaults) 579 580 class DecimalField(Field): 581 empty_strings_allowed = False 582 def __init__(self, verbose_name=None, name=None, max_digits=None, decimal_places=None, **kwargs): 583 self.max_digits, self.decimal_places = max_digits, decimal_places 584 Field.__init__(self, verbose_name, name, **kwargs) 585 586 def to_python(self, value): 587 if value is None: 588 return value 589 try: 590 return decimal.Decimal(value) 591 except decimal.InvalidOperation: 592 raise validators.ValidationError, gettext("This value must be a decimal number.") 593 594 def _format(self, value): 595 if isinstance(value, basestring): 596 return value 597 else: 598 return self.format_number(value) 599 600 def format_number(self, value): 601 """ 602 Formats a number into a string with the requisite number of digits and 603 decimal places. 604 """ 605 num_chars = self.max_digits 606 # Allow for a decimal point 607 if self.decimal_places > 0: 608 num_chars += 1 609 # Allow for a minus sign 610 if value < 0: 611 num_chars += 1 612 613 return "%.*f" % (self.decimal_places, value) 614 615 def get_db_prep_save(self, value): 616 if value is not None: 617 value = self._format(value) 618 return super(DecimalField, self).get_db_prep_save(value) 619 620 def get_db_prep_lookup(self, lookup_type, value): 621 if lookup_type == 'range': 622 value = [self._format(v) for v in value] 623 else: 624 value = self._format(value) 625 return super(DecimalField, self).get_db_prep_lookup(lookup_type, value) 626 627 def get_manipulator_field_objs(self): 628 return [curry(oldforms.DecimalField, max_digits=self.max_digits, decimal_places=self.decimal_places)] 629 630 def formfield(self, **kwargs): 631 defaults = { 632 'max_digits': self.max_digits, 633 'decimal_places': self.decimal_places, 634 'form_class': forms.DecimalField, 635 } 636 defaults.update(kwargs) 637 return super(DecimalField, self).formfield(**defaults) 575 638 576 639 class EmailField(CharField): … … 684 747 class FloatField(Field): 685 748 empty_strings_allowed = False 686 def __init__(self, verbose_name=None, name=None, max_digits=None, decimal_places=None, **kwargs): 687 self.max_digits, self.decimal_places = max_digits, decimal_places 688 Field.__init__(self, verbose_name, name, **kwargs) 689 690 def get_manipulator_field_objs(self): 691 return [curry(oldforms.FloatField, max_digits=self.max_digits, decimal_places=self.decimal_places)] 749 750 def get_manipulator_field_objs(self): 751 return [oldforms.FloatField] 752 753 def formfield(self, **kwargs): 754 defaults = {'form_class': forms.FloatField} 755 defaults.update(kwargs) 756 return super(FloatField, self).formfield(**defaults) 692 757 693 758 class ImageField(FileField): django/trunk/django/newforms/fields.py
r5263 r5302 20 20 'RegexField', 'EmailField', 'URLField', 'BooleanField', 21 21 'ChoiceField', 'NullBooleanField', 'MultipleChoiceField', 22 'ComboField', 'MultiValueField', 22 'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', 23 23 'SplitDateTimeField', 24 24 ) … … 31 31 except NameError: 32 32 from sets import Set as set # Python 2.3 fallback 33 34 try: 35 from decimal import Decimal 36 except ImportError: 37 from django.utils._decimal import Decimal # Python 2.3 fallback 33 38 34 39 class Field(object): … … 133 138 if self.min_value is not None and value < self.min_value: 134 139 raise ValidationError(gettext(u'Ensure this value is greater than or equal to %s.') % self.min_value) 140 return value 141 142 class FloatField(Field): 143 def __init__(self, max_value=None, min_value=None, *args, **kwargs): 144 self.max_value, self.min_value = max_value, min_value 145 Field.__init__(self, *args, **kwargs) 146 147 def clean(self, value): 148 """ 149 Validates that float() can be called on the input. Returns a float. 150 Returns None for empty values. 151 """ 152 super(FloatField, self).clean(value) 153 if not self.required and value in EMPTY_VALUES: 154 return None 155 try: 156 value = float(value) 157 except (ValueError, TypeError): 158 raise ValidationError(gettext('Enter a number.')) 159 if self.max_value is not None and value > self.max_value: 160 raise ValidationError(gettext('Ensure this value is less than or equal to %s.') % self.max_value) 161 if self.min_value is not None and value < self.min_value: 162 raise ValidationError(gettext('Ensure this value is greater than or equal to %s.') % self.min_value) 163 return value 164 165 decimal_re = re.compile(r'^-?(?P<digits>\d+)(\.(?P<decimals>\d+))?$') 166 167 class DecimalField(Field): 168 def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs): 169 self.max_value, self.min_value = max_value, min_value 170 self.max_digits, self.decimal_places = max_digits, decimal_places 171 Field.__init__(self, *args, **kwargs) 172 173 def clean(self, value): 174 """ 175 Validates that the input is a decimal number. Returns a Decimal 176 instance. Returns None for empty values. Ensures that there are no more 177 than max_digits in the number, and no more than decimal_places digits 178 after the decimal point. 179 """ 180 super(DecimalField, self).clean(value) 181 if not self.required and value in EMPTY_VALUES: 182 return None 183 value = value.strip() 184 match = decimal_re.search(value) 185 if not match: 186 raise ValidationError(gettext('Enter a number.')) 187 else: 188 value = Decimal(value) 189 digits = len(match.group('digits') or '') 190 decimals = len(match.group('decimals') or '') 191 if self.max_value is not None and value > self.max_value: 192 raise ValidationError(gettext('Ensure this value is less than or equal to %s.') % self.max_value) 193 if self.min_value is not None and value < self.min_value: 194 raise ValidationError(gettext('Ensure this value is greater than or equal to %s.') % self.min_value) 195 if self.max_digits is not None and (digits + decimals) > self.max_digits: 196 raise ValidationError(gettext('Ensure that there are no more than %s digits in total.') % self.max_digits) 197 if self.decimal_places is not None and decimals > self.decimal_places: 198 raise ValidationError(gettext('Ensure that there are no more than %s decimal places.') % self.decimal_places) 199 if self.max_digits is not None and self.decimal_places is not None and digits > (self.max_digits - self.decimal_places): 200 raise ValidationError(gettext('Ensure that there are no more than %s digits before the decimal point.') % (self.max_digits - self.decimal_places)) 135 201 return value 136 202 django/trunk/django/oldforms/__init__.py
r5091 r5302 751 751 752 752 class FloatField(TextField): 753 def __init__(self, field_name, is_required=False, validator_list=None): 754 if validator_list is None: validator_list = [] 755 validator_list = [validators.isValidFloat] + validator_list 756 TextField.__init__(self, field_name, is_required=is_required, validator_list=validator_list) 757 758 def html2python(data): 759 if data == '' or data is None: 760 return None 761 return float(data) 762 html2python = staticmethod(html2python) 763 764 class DecimalField(TextField): 753 765 def __init__(self, field_name, max_digits, decimal_places, is_required=False, validator_list=None): 754 766 if validator_list is None: validator_list = [] 755 767 self.max_digits, self.decimal_places = max_digits, decimal_places 756 validator_list = [self.isValidFloat] + validator_list 757 TextField.__init__(self, field_name, max_digits+2, max_digits+2, is_required, validator_list) 758 759 def isValidFloat(self, field_data, all_data): 760 v = validators.IsValidFloat(self.max_digits, self.decimal_places) 768 validator_list = [self.isValidDecimal] + validator_list 769 # Initialise the TextField, making sure it's large enough to fit the number with a - sign and a decimal point. 770 super(DecimalField, self).__init__(field_name, max_digits+2, max_digits+2, is_required, validator_list) 771 772 def isValidDecimal(self, field_data, all_data): 773 v = validators.IsValidDecimal(self.max_digits, self.decimal_places) 761 774 try: 762 775 v(field_data, all_data) … … 767 780 if data == '' or data is None: 768 781 return None 769 return float(data) 782 try: 783 import decimal 784 except ImportError: 785 from django.utils import decimal 786 try: 787 return decimal.Decimal(data) 788 except decimal.InvalidOperation, e: 789 raise ValueError, e 770 790 html2python = staticmethod(html2python) 771 791 django/trunk/docs/forms.txt
r5113 r5302 568 568 * isValidANSITime 569 569 * isValidEmail 570 * isValidFloat 570 571 * isValidImage 571 572 * isValidImageURL … … 665 666 field being validated is a power of the integer. 666 667 667 ``IsValid Float``668 ``IsValidDecimal`` 668 669 Takes a maximum number of digits and number of decimal places (in that 669 order) and validates whether the field is a float with lessthan the670 maximum number of digits and decimal place .670 order) and validates whether the field is a decimal with no more than the 671 maximum number of digits and decimal places. 671 672 672 673 ``MatchesRegularExpression`` django/trunk/docs/model-api.txt
r5290 r5302 185 185 JavaScript shortcuts. 186 186 187 ``DecimalField`` 188 ~~~~~~~~~~~~~~ 189 190 A fixed-precision decimal number, represented in Python by a ``Decimal`` instance. 191 Has two **required** arguments: 192 193 ====================== =================================================== 194 Argument Description 195 ====================== =================================================== 196 ``max_digits`` The maximum number of digits allowed in the number. 197 198 ``decimal_places`` The number of decimal places to store with the 199 number. 200 ====================== =================================================== 201 202 For example, to store numbers up to 999 with a resolution of 2 decimal places, 203 you'd use:: 204 205 models.DecimalField(..., max_digits=5, decimal_places=2) 206 207 And to store numbers up to approximately one billion with a resolution of 10 208 decimal places:: 209 210 models.DecimalField(..., max_digits=19, decimal_places=10) 211 212 The admin represents this as an ``<input type="text">`` (a single-line input). 213 187 214 ``EmailField`` 188 215 ~~~~~~~~~~~~~~ … … 291 318 ~~~~~~~~~~~~~~ 292 319 293 A floating-point number. Has two **required** arguments: 294 295 ====================== =================================================== 296 Argument Description 297 ====================== =================================================== 298 ``max_digits`` The maximum number of digits allowed in the number. 299 300 ``decimal_places`` The number of decimal places to store with the 301 number. 302 ====================== =================================================== 303 304 For example, to store numbers up to 999 with a resolution of 2 decimal places, 305 you'd use:: 306 307 models.FloatField(..., max_digits=5, decimal_places=2) 308 309 And to store numbers up to approximately one billion with a resolution of 10 310 decimal places:: 311 312 models.FloatField(..., max_digits=19, decimal_places=10) 320 A floating-point number represented in Python by a ``float`` instance. 313 321 314 322 The admin represents this as an ``<input type="text">`` (a single-line input). django/trunk/docs/newforms.txt
r5299 r5302 1254 1254 ``DateField`` ``DateField`` 1255 1255 ``DateTimeField`` ``DateTimeField`` 1256 ``DecimalField`` ``DecimalField`` 1256 1257 ``EmailField`` ``EmailField`` 1257 1258 ``FileField`` ``CharField`` 1258 1259 ``FilePathField`` ``CharField`` 1259 ``FloatField`` `` CharField``1260 ``FloatField`` ``FloatField`` 1260 1261 ``ForeignKey`` ``ModelChoiceField`` (see below) 1261 1262 ``ImageField`` ``CharField`` … … 1282 1283 =============================== ======================================== 1283 1284 1285 1286 .. note:: 1287 The ``FloatField`` form field and ``DecimalField`` model and form fields 1288 are new in the development version.
