Commit 1506c71a authored by Simon Charette's avatar Simon Charette
Browse files

Fixed #12030 -- Validate integer field range at the model level.

Thanks to @timgraham for the review.
parent 9b7ba8af
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -617,6 +617,16 @@ class BaseDatabaseOperations(object):
    """
    compiler_module = "django.db.models.sql.compiler"

    # Integer field safe ranges by `internal_type` as documented
    # in docs/ref/models/fields.txt.
    integer_field_ranges = {
        'SmallIntegerField': (-32768, 32767),
        'IntegerField': (-2147483648, 2147483647),
        'BigIntegerField': (-9223372036854775808, 9223372036854775807),
        'PositiveSmallIntegerField': (0, 32767),
        'PositiveIntegerField': (0, 2147483647),
    }

    def __init__(self, connection):
        self.connection = connection
        self._cache = None
@@ -1101,6 +1111,14 @@ class BaseDatabaseOperations(object):
        """
        return params

    def integer_field_range(self, internal_type):
        """
        Given an integer field internal type (e.g. 'PositiveIntegerField'),
        returns a tuple of the (min_value, max_value) form representing the
        range of the column type bound to the field.
        """
        return self.integer_field_ranges[internal_type]


# Structure returned by the DB-API cursor.description interface (PEP 249)
FieldInfo = namedtuple('FieldInfo',
+6 −0
Original line number Diff line number Diff line
@@ -223,6 +223,12 @@ class DatabaseFeatures(BaseDatabaseFeatures):
class DatabaseOperations(BaseDatabaseOperations):
    compiler_module = "django.db.backends.mysql.compiler"

    # MySQL stores positive fields as UNSIGNED ints.
    integer_field_ranges = dict(BaseDatabaseOperations.integer_field_ranges,
        PositiveSmallIntegerField=(0, 4294967295),
        PositiveIntegerField=(0, 18446744073709551615),
    )

    def date_extract_sql(self, lookup_type, field_name):
        # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
        if lookup_type == 'week_day':
+9 −0
Original line number Diff line number Diff line
@@ -121,6 +121,15 @@ class DatabaseFeatures(BaseDatabaseFeatures):
class DatabaseOperations(BaseDatabaseOperations):
    compiler_module = "django.db.backends.oracle.compiler"

    # Oracle uses NUMBER(11) and NUMBER(19) for integer fields.
    integer_field_ranges = {
        'SmallIntegerField': (-99999999999, 99999999999),
        'IntegerField': (-99999999999, 99999999999),
        'BigIntegerField': (-9999999999999999999, 9999999999999999999),
        'PositiveSmallIntegerField': (0, 99999999999),
        'PositiveIntegerField': (0, 99999999999),
    }

    def autoinc_sql(self, table, column):
        # To simulate auto-incrementing primary keys in Oracle, we have to
        # create a sequence and a trigger.
+4 −0
Original line number Diff line number Diff line
@@ -292,6 +292,10 @@ class DatabaseOperations(BaseDatabaseOperations):
            return 'django_power(%s)' % ','.join(sub_expressions)
        return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)

    def integer_field_range(self, internal_type):
        # SQLite doesn't enforce any integer constraints
        return (None, None)


class DatabaseWrapper(BaseDatabaseWrapper):
    vendor = 'sqlite'
+10 −0
Original line number Diff line number Diff line
@@ -1561,6 +1561,16 @@ class IntegerField(Field):
    }
    description = _("Integer")

    def __init__(self, *args, **kwargs):
        field_validators = kwargs.setdefault('validators', [])
        internal_type = self.get_internal_type()
        min_value, max_value = connection.ops.integer_field_range(internal_type)
        if min_value is not None:
            field_validators.append(validators.MinValueValidator(min_value))
        if max_value is not None:
            field_validators.append(validators.MaxValueValidator(max_value))
        super(IntegerField, self).__init__(*args, **kwargs)

    def get_prep_value(self, value):
        value = super(IntegerField, self).get_prep_value(value)
        if value is None:
Loading