Fixed #30987 -- Added models.PositiveBigIntegerField.

This commit is contained in:
Caio Ariede 2019-10-16 09:32:12 -03:00 committed by Mariusz Felisiak
parent aa12cf07c9
commit 555bebe774
28 changed files with 112 additions and 11 deletions

View File

@ -77,6 +77,7 @@ class LayerMapping:
models.UUIDField: OFTString,
models.BigIntegerField: (OFTInteger, OFTReal, OFTString),
models.SmallIntegerField: (OFTInteger, OFTReal, OFTString),
models.PositiveBigIntegerField: (OFTInteger, OFTReal, OFTString),
models.PositiveIntegerField: (OFTInteger, OFTReal, OFTString),
models.PositiveSmallIntegerField: (OFTInteger, OFTReal, OFTString),
}

View File

@ -24,6 +24,7 @@ class BaseDatabaseOperations:
'SmallIntegerField': (-32768, 32767),
'IntegerField': (-2147483648, 2147483647),
'BigIntegerField': (-9223372036854775808, 9223372036854775807),
'PositiveBigIntegerField': (0, 9223372036854775807),
'PositiveSmallIntegerField': (0, 32767),
'PositiveIntegerField': (0, 2147483647),
'SmallAutoField': (-32768, 32767),

View File

@ -120,6 +120,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveBigIntegerField': 'bigint UNSIGNED',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
@ -339,6 +340,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
def data_type_check_constraints(self):
if self.features.supports_column_check_constraints:
return {
'PositiveBigIntegerField': '`%(column)s` >= 0',
'PositiveIntegerField': '`%(column)s` >= 0',
'PositiveSmallIntegerField': '`%(column)s` >= 0',
}

View File

@ -47,7 +47,9 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
elif field_type == 'SmallIntegerField':
return 'SmallAutoField'
if description.is_unsigned:
if field_type == 'IntegerField':
if field_type == 'BigIntegerField':
return 'PositiveBigIntegerField'
elif field_type == 'IntegerField':
return 'PositiveIntegerField'
elif field_type == 'SmallIntegerField':
return 'PositiveSmallIntegerField'

View File

@ -15,6 +15,7 @@ class DatabaseOperations(BaseDatabaseOperations):
**BaseDatabaseOperations.integer_field_ranges,
'PositiveSmallIntegerField': (0, 65535),
'PositiveIntegerField': (0, 4294967295),
'PositiveBigIntegerField': (0, 18446744073709551615),
}
cast_data_types = {
'AutoField': 'signed integer',
@ -26,6 +27,7 @@ class DatabaseOperations(BaseDatabaseOperations):
'IntegerField': 'signed integer',
'BigIntegerField': 'signed integer',
'SmallIntegerField': 'signed integer',
'PositiveBigIntegerField': 'unsigned integer',
'PositiveIntegerField': 'unsigned integer',
'PositiveSmallIntegerField': 'unsigned integer',
}

View File

@ -120,6 +120,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
'GenericIPAddressField': 'VARCHAR2(39)',
'NullBooleanField': 'NUMBER(1)',
'OneToOneField': 'NUMBER(11)',
'PositiveBigIntegerField': 'NUMBER(19)',
'PositiveIntegerField': 'NUMBER(11)',
'PositiveSmallIntegerField': 'NUMBER(11)',
'SlugField': 'NVARCHAR2(%(max_length)s)',
@ -133,6 +134,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
data_type_check_constraints = {
'BooleanField': '%(qn_column)s IN (0,1)',
'NullBooleanField': '%(qn_column)s IN (0,1)',
'PositiveBigIntegerField': '%(qn_column)s >= 0',
'PositiveIntegerField': '%(qn_column)s >= 0',
'PositiveSmallIntegerField': '%(qn_column)s >= 0',
}

View File

@ -25,6 +25,7 @@ class DatabaseOperations(BaseDatabaseOperations):
'SmallIntegerField': (-99999999999, 99999999999),
'IntegerField': (-99999999999, 99999999999),
'BigIntegerField': (-9999999999999999999, 9999999999999999999),
'PositiveBigIntegerField': (0, 9999999999999999999),
'PositiveSmallIntegerField': (0, 99999999999),
'PositiveIntegerField': (0, 99999999999),
'SmallAutoField': (-99999, 99999),

View File

@ -16,6 +16,7 @@ class InsertVar:
'IntegerField': int,
'BigIntegerField': int,
'SmallIntegerField': int,
'PositiveBigIntegerField': int,
'PositiveSmallIntegerField': int,
'PositiveIntegerField': int,
'FloatField': Database.NATIVE_FLOAT,
@ -71,6 +72,7 @@ class BulkInsertMapper:
'FloatField': NUMBER,
'IntegerField': NUMBER,
'NullBooleanField': NUMBER,
'PositiveBigIntegerField': NUMBER,
'PositiveIntegerField': NUMBER,
'PositiveSmallIntegerField': NUMBER,
'SmallIntegerField': NUMBER,

View File

@ -89,6 +89,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
'GenericIPAddressField': 'inet',
'NullBooleanField': 'boolean',
'OneToOneField': 'integer',
'PositiveBigIntegerField': 'bigint',
'PositiveIntegerField': 'integer',
'PositiveSmallIntegerField': 'smallint',
'SlugField': 'varchar(%(max_length)s)',
@ -99,6 +100,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
'UUIDField': 'uuid',
}
data_type_check_constraints = {
'PositiveBigIntegerField': '"%(column)s" >= 0',
'PositiveIntegerField': '"%(column)s" >= 0',
'PositiveSmallIntegerField': '"%(column)s" >= 0',
}

View File

@ -102,6 +102,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveBigIntegerField': 'bigint unsigned',
'PositiveIntegerField': 'integer unsigned',
'PositiveSmallIntegerField': 'smallint unsigned',
'SlugField': 'varchar(%(max_length)s)',
@ -112,6 +113,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
'UUIDField': 'char(32)',
}
data_type_check_constraints = {
'PositiveBigIntegerField': '"%(column)s" >= 0',
'PositiveIntegerField': '"%(column)s" >= 0',
'PositiveSmallIntegerField': '"%(column)s" >= 0',
}

View File

@ -37,6 +37,7 @@ class FlexibleFieldLookupDict:
'integer': 'IntegerField',
'bigint': 'BigIntegerField',
'integer unsigned': 'PositiveIntegerField',
'bigint unsigned': 'PositiveBigIntegerField',
'decimal': 'DecimalField',
'real': 'FloatField',
'text': 'TextField',

View File

@ -33,9 +33,9 @@ __all__ = [
'DateField', 'DateTimeField', 'DecimalField', 'DurationField',
'EmailField', 'Empty', 'Field', 'FilePathField', 'FloatField',
'GenericIPAddressField', 'IPAddressField', 'IntegerField', 'NOT_PROVIDED',
'NullBooleanField', 'PositiveIntegerField', 'PositiveSmallIntegerField',
'SlugField', 'SmallAutoField', 'SmallIntegerField', 'TextField',
'TimeField', 'URLField', 'UUIDField',
'NullBooleanField', 'PositiveBigIntegerField', 'PositiveIntegerField',
'PositiveSmallIntegerField', 'SlugField', 'SmallAutoField',
'SmallIntegerField', 'TextField', 'TimeField', 'URLField', 'UUIDField',
]
@ -1955,6 +1955,19 @@ class PositiveIntegerRelDbTypeMixin:
return IntegerField().db_type(connection=connection)
class PositiveBigIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField):
description = _('Positive big integer')
def get_internal_type(self):
return 'PositiveBigIntegerField'
def formfield(self, **kwargs):
return super().formfield(**{
'min_value': 0,
**kwargs,
})
class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField):
description = _("Positive integer")

View File

@ -1177,6 +1177,17 @@ values are stored as null.
Like :class:`BooleanField` with ``null=True``. Use that instead of this field
as it's likely to be deprecated in a future version of Django.
``PositiveBigIntegerField``
---------------------------
.. class:: PositiveBigIntegerField(**options)
.. versionadded:: 3.1
Like a :class:`PositiveIntegerField`, but only allows values under a certain
(database-dependent) point. Values from ``0`` to ``9223372036854775807`` are
safe in all databases supported by Django.
``PositiveIntegerField``
------------------------

View File

@ -194,6 +194,11 @@ Models
* ``TREE`` format on MySQL 8.0.16+,
* ``analyze`` option on MySQL 8.0.18+ and MariaDB.
* Added :class:`~django.db.models.PositiveBigIntegerField` which acts much like
a :class:`~django.db.models.PositiveIntegerField` except that it only allows
values under a certain (database-dependent) limit. Values from ``0`` to
``9223372036854775807`` are safe in all databases supported by Django.
Pagination
~~~~~~~~~~

View File

@ -106,6 +106,8 @@ Model field Form field
:class:`NullBooleanField` :class:`~django.forms.NullBooleanField`
:class:`PositiveBigIntegerField` :class:`~django.forms.IntegerField`
:class:`PositiveIntegerField` :class:`~django.forms.IntegerField`
:class:`PositiveSmallIntegerField` :class:`~django.forms.IntegerField`

View File

@ -76,6 +76,7 @@ class NullableFields(models.Model):
integer_field = models.IntegerField(null=True, default=2)
null_boolean_field = models.BooleanField(null=True, default=False)
null_boolean_field_old = models.NullBooleanField(null=True, default=False)
positive_big_integer_field = models.PositiveBigIntegerField(null=True, default=2 ** 63 - 1)
positive_integer_field = models.PositiveIntegerField(null=True, default=3)
positive_small_integer_field = models.PositiveSmallIntegerField(null=True, default=4)
small_integer_field = models.SmallIntegerField(null=True, default=5)

View File

@ -59,6 +59,7 @@ class CastTests(TestCase):
models.IntegerField,
models.BigIntegerField,
models.SmallIntegerField,
models.PositiveBigIntegerField,
models.PositiveIntegerField,
models.PositiveSmallIntegerField,
):

View File

@ -29,6 +29,7 @@ class CaseTestModel(models.Model):
null_boolean_old = models.NullBooleanField()
positive_integer = models.PositiveIntegerField(null=True)
positive_small_integer = models.PositiveSmallIntegerField(null=True)
positive_big_integer = models.PositiveSmallIntegerField(null=True)
slug = models.SlugField(default='')
small_integer = models.SmallIntegerField(null=True)
text = models.TextField(default='')

View File

@ -833,6 +833,19 @@ class CaseExpressionTests(TestCase):
transform=attrgetter('integer', 'null_boolean_old')
)
def test_update_positive_big_integer(self):
CaseTestModel.objects.update(
positive_big_integer=Case(
When(integer=1, then=1),
When(integer=2, then=2),
),
)
self.assertQuerysetEqual(
CaseTestModel.objects.all().order_by('pk'),
[(1, 1), (2, 2), (3, None), (2, 2), (3, None), (3, None), (4, None)],
transform=attrgetter('integer', 'positive_big_integer')
)
def test_update_positive_integer(self):
CaseTestModel.objects.update(
positive_integer=Case(

View File

@ -453,6 +453,13 @@ class FieldDeconstructionTests(SimpleTestCase):
self.assertEqual(args, [])
self.assertEqual(kwargs, {})
def test_positive_big_integer_field(self):
field = models.PositiveBigIntegerField()
name, path, args, kwargs = field.deconstruct()
self.assertEqual(path, 'django.db.models.PositiveBigIntegerField')
self.assertEqual(args, [])
self.assertEqual(kwargs, {})
def test_slug_field(self):
field = models.SlugField()
name, path, args, kwargs = field.deconstruct()

View File

@ -57,6 +57,7 @@ class ColumnTypes(models.Model):
float_field = models.FloatField()
int_field = models.IntegerField()
gen_ip_address_field = models.GenericIPAddressField(protocol="ipv4")
pos_big_int_field = models.PositiveBigIntegerField()
pos_int_field = models.PositiveIntegerField()
pos_small_int_field = models.PositiveSmallIntegerField()
slug_field = models.SlugField()

View File

@ -118,11 +118,19 @@ class InspectDBTestCase(TestCase):
assertFieldType('pos_int_field', "models.IntegerField()")
if connection.features.can_introspect_positive_integer_field:
if connection.features.can_introspect_big_integer_field:
assertFieldType('pos_big_int_field', 'models.PositiveBigIntegerField()')
else:
assertFieldType('pos_big_int_field', 'models.PositiveIntegerField()')
if connection.features.can_introspect_small_integer_field:
assertFieldType('pos_small_int_field', "models.PositiveSmallIntegerField()")
else:
assertFieldType('pos_small_int_field', "models.PositiveIntegerField()")
else:
if connection.features.can_introspect_big_integer_field:
assertFieldType('pos_big_int_field', 'models.BigIntegerField()')
else:
assertFieldType('pos_big_int_field', 'models.IntegerField()')
if connection.features.can_introspect_small_integer_field:
assertFieldType('pos_small_int_field', "models.SmallIntegerField()")
else:

View File

@ -689,6 +689,7 @@ class IntegerFieldTests(SimpleTestCase):
biginteger = models.BigIntegerField(max_length=2)
smallinteger = models.SmallIntegerField(max_length=2)
positiveinteger = models.PositiveIntegerField(max_length=2)
positivebiginteger = models.PositiveBigIntegerField(max_length=2)
positivesmallinteger = models.PositiveSmallIntegerField(max_length=2)
for field in Model._meta.get_fields():

View File

@ -117,6 +117,10 @@ class BigIntegerModel(models.Model):
null_value = models.BigIntegerField(null=True, blank=True)
class PositiveBigIntegerModel(models.Model):
value = models.PositiveBigIntegerField()
class PositiveSmallIntegerModel(models.Model):
value = models.PositiveSmallIntegerField()

View File

@ -6,8 +6,8 @@ from django.db import IntegrityError, connection, models
from django.test import SimpleTestCase, TestCase
from .models import (
BigIntegerModel, IntegerModel, PositiveIntegerModel,
PositiveSmallIntegerModel, SmallIntegerModel,
BigIntegerModel, IntegerModel, PositiveBigIntegerModel,
PositiveIntegerModel, PositiveSmallIntegerModel, SmallIntegerModel,
)
@ -182,6 +182,11 @@ class PositiveIntegerFieldTests(IntegerFieldTests):
p.save()
class PositiveBigIntegerFieldTests(IntegerFieldTests):
model = PositiveBigIntegerModel
documented_range = (0, 9223372036854775807)
class ValidationTests(SimpleTestCase):
class Choices(models.IntegerChoices):

View File

@ -4,9 +4,9 @@ from decimal import Decimal
from django.db.models.fields import (
AutoField, BinaryField, BooleanField, CharField, DateField, DateTimeField,
DecimalField, EmailField, FilePathField, FloatField, GenericIPAddressField,
IntegerField, IPAddressField, NullBooleanField, PositiveIntegerField,
PositiveSmallIntegerField, SlugField, SmallIntegerField, TextField,
TimeField, URLField,
IntegerField, IPAddressField, NullBooleanField, PositiveBigIntegerField,
PositiveIntegerField, PositiveSmallIntegerField, SlugField,
SmallIntegerField, TextField, TimeField, URLField,
)
from django.db.models.fields.files import FileField, ImageField
from django.test import SimpleTestCase
@ -97,6 +97,10 @@ class PromiseTest(SimpleTestCase):
lazy_func = lazy(lambda: 1, int)
self.assertIsInstance(PositiveSmallIntegerField().get_prep_value(lazy_func()), int)
def test_PositiveBigIntegerField(self):
lazy_func = lazy(lambda: 1, int)
self.assertIsInstance(PositiveBigIntegerField().get_prep_value(lazy_func()), int)
def test_SlugField(self):
lazy_func = lazy(lambda: 'slug', str)
self.assertIsInstance(SlugField().get_prep_value(lazy_func()), str)

View File

@ -72,6 +72,10 @@ class NullBooleanData(models.Model):
data = models.NullBooleanField(null=True)
class PositiveBigIntegerData(models.Model):
data = models.PositiveBigIntegerField(null=True)
class PositiveIntegerData(models.Model):
data = models.PositiveIntegerField(null=True)

View File

@ -23,8 +23,8 @@ from .models import (
GenericData, GenericIPAddressData, GenericIPAddressPKData,
InheritAbstractModel, InheritBaseModel, IntegerData, IntegerPKData,
Intermediate, LengthModel, M2MData, M2MIntermediateData, M2MSelfData,
ModifyingSaveData, NullBooleanData, O2OData, PositiveIntegerData,
PositiveIntegerPKData, PositiveSmallIntegerData,
ModifyingSaveData, NullBooleanData, O2OData, PositiveBigIntegerData,
PositiveIntegerData, PositiveIntegerPKData, PositiveSmallIntegerData,
PositiveSmallIntegerPKData, SlugData, SlugPKData, SmallData, SmallPKData,
Tag, TextData, TimeData, UniqueAnchor, UUIDData,
)
@ -241,6 +241,8 @@ test_data = [
(data_obj, 100, NullBooleanData, True),
(data_obj, 101, NullBooleanData, False),
(data_obj, 102, NullBooleanData, None),
(data_obj, 110, PositiveBigIntegerData, 9223372036854775807),
(data_obj, 111, PositiveBigIntegerData, None),
(data_obj, 120, PositiveIntegerData, 123456789),
(data_obj, 121, PositiveIntegerData, None),
(data_obj, 130, PositiveSmallIntegerData, 12),