Fixed #399: Added big integer field. Thanks to Tomáš Kopeček for persistently maintaining a patch for this.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@11887 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
fd4cc65baf
commit
5bd63663a9
1
AUTHORS
1
AUTHORS
|
@ -246,6 +246,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Cameron Knight (ckknight)
|
Cameron Knight (ckknight)
|
||||||
Nena Kojadin <nena@kiberpipa.org>
|
Nena Kojadin <nena@kiberpipa.org>
|
||||||
Igor Kolar <ike@email.si>
|
Igor Kolar <ike@email.si>
|
||||||
|
Tomáš Kopeček <permonik@m6.cz>
|
||||||
Gasper Koren
|
Gasper Koren
|
||||||
Martin Kosír <martin@martinkosir.net>
|
Martin Kosír <martin@martinkosir.net>
|
||||||
Arthur Koziel <http://arthurkoziel.com>
|
Arthur Koziel <http://arthurkoziel.com>
|
||||||
|
|
|
@ -42,14 +42,15 @@ FORMFIELD_FOR_DBFIELD_DEFAULTS = {
|
||||||
'form_class': forms.SplitDateTimeField,
|
'form_class': forms.SplitDateTimeField,
|
||||||
'widget': widgets.AdminSplitDateTime
|
'widget': widgets.AdminSplitDateTime
|
||||||
},
|
},
|
||||||
models.DateField: {'widget': widgets.AdminDateWidget},
|
models.DateField: {'widget': widgets.AdminDateWidget},
|
||||||
models.TimeField: {'widget': widgets.AdminTimeWidget},
|
models.TimeField: {'widget': widgets.AdminTimeWidget},
|
||||||
models.TextField: {'widget': widgets.AdminTextareaWidget},
|
models.TextField: {'widget': widgets.AdminTextareaWidget},
|
||||||
models.URLField: {'widget': widgets.AdminURLFieldWidget},
|
models.URLField: {'widget': widgets.AdminURLFieldWidget},
|
||||||
models.IntegerField: {'widget': widgets.AdminIntegerFieldWidget},
|
models.IntegerField: {'widget': widgets.AdminIntegerFieldWidget},
|
||||||
models.CharField: {'widget': widgets.AdminTextInputWidget},
|
models.BigIntegerField: {'widget': widgets.AdminIntegerFieldWidget},
|
||||||
models.ImageField: {'widget': widgets.AdminFileWidget},
|
models.CharField: {'widget': widgets.AdminTextInputWidget},
|
||||||
models.FileField: {'widget': widgets.AdminFileWidget},
|
models.ImageField: {'widget': widgets.AdminFileWidget},
|
||||||
|
models.FileField: {'widget': widgets.AdminFileWidget},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ class DatabaseCreation(BaseDatabaseCreation):
|
||||||
'FilePathField': 'varchar(%(max_length)s)',
|
'FilePathField': 'varchar(%(max_length)s)',
|
||||||
'FloatField': 'double precision',
|
'FloatField': 'double precision',
|
||||||
'IntegerField': 'integer',
|
'IntegerField': 'integer',
|
||||||
|
'BigIntegerField': 'bigint',
|
||||||
'IPAddressField': 'char(15)',
|
'IPAddressField': 'char(15)',
|
||||||
'NullBooleanField': 'bool',
|
'NullBooleanField': 'bool',
|
||||||
'OneToOneField': 'integer',
|
'OneToOneField': 'integer',
|
||||||
|
|
|
@ -17,7 +17,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
|
||||||
FIELD_TYPE.FLOAT: 'FloatField',
|
FIELD_TYPE.FLOAT: 'FloatField',
|
||||||
FIELD_TYPE.INT24: 'IntegerField',
|
FIELD_TYPE.INT24: 'IntegerField',
|
||||||
FIELD_TYPE.LONG: 'IntegerField',
|
FIELD_TYPE.LONG: 'IntegerField',
|
||||||
FIELD_TYPE.LONGLONG: 'IntegerField',
|
FIELD_TYPE.LONGLONG: 'BigIntegerField',
|
||||||
FIELD_TYPE.SHORT: 'IntegerField',
|
FIELD_TYPE.SHORT: 'IntegerField',
|
||||||
FIELD_TYPE.STRING: 'CharField',
|
FIELD_TYPE.STRING: 'CharField',
|
||||||
FIELD_TYPE.TIMESTAMP: 'DateTimeField',
|
FIELD_TYPE.TIMESTAMP: 'DateTimeField',
|
||||||
|
|
|
@ -27,6 +27,7 @@ class DatabaseCreation(BaseDatabaseCreation):
|
||||||
'FilePathField': 'NVARCHAR2(%(max_length)s)',
|
'FilePathField': 'NVARCHAR2(%(max_length)s)',
|
||||||
'FloatField': 'DOUBLE PRECISION',
|
'FloatField': 'DOUBLE PRECISION',
|
||||||
'IntegerField': 'NUMBER(11)',
|
'IntegerField': 'NUMBER(11)',
|
||||||
|
'BigIntegerField': 'NUMBER(19)',
|
||||||
'IPAddressField': 'VARCHAR2(15)',
|
'IPAddressField': 'VARCHAR2(15)',
|
||||||
'NullBooleanField': 'NUMBER(1) CHECK ((%(qn_column)s IN (0,1)) OR (%(qn_column)s IS NULL))',
|
'NullBooleanField': 'NUMBER(1) CHECK ((%(qn_column)s IN (0,1)) OR (%(qn_column)s IS NULL))',
|
||||||
'OneToOneField': 'NUMBER(11)',
|
'OneToOneField': 'NUMBER(11)',
|
||||||
|
|
|
@ -29,7 +29,10 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
|
||||||
def get_field_type(self, data_type, description):
|
def get_field_type(self, data_type, description):
|
||||||
# If it's a NUMBER with scale == 0, consider it an IntegerField
|
# If it's a NUMBER with scale == 0, consider it an IntegerField
|
||||||
if data_type == cx_Oracle.NUMBER and description[5] == 0:
|
if data_type == cx_Oracle.NUMBER and description[5] == 0:
|
||||||
return 'IntegerField'
|
if description[4] > 11:
|
||||||
|
return 'BigIntegerField'
|
||||||
|
else:
|
||||||
|
return 'IntegerField'
|
||||||
else:
|
else:
|
||||||
return super(DatabaseIntrospection, self).get_field_type(
|
return super(DatabaseIntrospection, self).get_field_type(
|
||||||
data_type, description)
|
data_type, description)
|
||||||
|
|
|
@ -18,6 +18,7 @@ class DatabaseCreation(BaseDatabaseCreation):
|
||||||
'FilePathField': 'varchar(%(max_length)s)',
|
'FilePathField': 'varchar(%(max_length)s)',
|
||||||
'FloatField': 'double precision',
|
'FloatField': 'double precision',
|
||||||
'IntegerField': 'integer',
|
'IntegerField': 'integer',
|
||||||
|
'BigIntegerField': 'bigint',
|
||||||
'IPAddressField': 'inet',
|
'IPAddressField': 'inet',
|
||||||
'NullBooleanField': 'boolean',
|
'NullBooleanField': 'boolean',
|
||||||
'OneToOneField': 'integer',
|
'OneToOneField': 'integer',
|
||||||
|
|
|
@ -4,6 +4,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
|
||||||
# Maps type codes to Django Field types.
|
# Maps type codes to Django Field types.
|
||||||
data_types_reverse = {
|
data_types_reverse = {
|
||||||
16: 'BooleanField',
|
16: 'BooleanField',
|
||||||
|
20: 'BigIntegerField',
|
||||||
21: 'SmallIntegerField',
|
21: 'SmallIntegerField',
|
||||||
23: 'IntegerField',
|
23: 'IntegerField',
|
||||||
25: 'TextField',
|
25: 'TextField',
|
||||||
|
|
|
@ -19,6 +19,7 @@ class DatabaseCreation(BaseDatabaseCreation):
|
||||||
'FilePathField': 'varchar(%(max_length)s)',
|
'FilePathField': 'varchar(%(max_length)s)',
|
||||||
'FloatField': 'real',
|
'FloatField': 'real',
|
||||||
'IntegerField': 'integer',
|
'IntegerField': 'integer',
|
||||||
|
'BigIntegerField': 'bigint',
|
||||||
'IPAddressField': 'char(15)',
|
'IPAddressField': 'char(15)',
|
||||||
'NullBooleanField': 'bool',
|
'NullBooleanField': 'bool',
|
||||||
'OneToOneField': 'integer',
|
'OneToOneField': 'integer',
|
||||||
|
|
|
@ -16,6 +16,7 @@ class FlexibleFieldLookupDict:
|
||||||
'smallinteger': 'SmallIntegerField',
|
'smallinteger': 'SmallIntegerField',
|
||||||
'int': 'IntegerField',
|
'int': 'IntegerField',
|
||||||
'integer': 'IntegerField',
|
'integer': 'IntegerField',
|
||||||
|
'bigint': 'BigIntegerField',
|
||||||
'integer unsigned': 'PositiveIntegerField',
|
'integer unsigned': 'PositiveIntegerField',
|
||||||
'decimal': 'DecimalField',
|
'decimal': 'DecimalField',
|
||||||
'real': 'FloatField',
|
'real': 'FloatField',
|
||||||
|
|
|
@ -732,6 +732,19 @@ class IntegerField(Field):
|
||||||
defaults.update(kwargs)
|
defaults.update(kwargs)
|
||||||
return super(IntegerField, self).formfield(**defaults)
|
return super(IntegerField, self).formfield(**defaults)
|
||||||
|
|
||||||
|
class BigIntegerField(IntegerField):
|
||||||
|
empty_strings_allowed = False
|
||||||
|
description = ugettext_lazy("Big (8 byte) integer")
|
||||||
|
MAX_BIGINT = 9223372036854775807
|
||||||
|
def get_internal_type(self):
|
||||||
|
return "BigIntegerField"
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'min_value': -BigIntegerField.MAX_BIGINT - 1,
|
||||||
|
'max_value': BigIntegerField.MAX_BIGINT}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return super(BigIntegerField, self).formfield(**defaults)
|
||||||
|
|
||||||
class IPAddressField(Field):
|
class IPAddressField(Field):
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
description = ugettext_lazy("IP address")
|
description = ugettext_lazy("IP address")
|
||||||
|
|
|
@ -299,6 +299,18 @@ according to available IDs. You usually won't need to use this directly; a
|
||||||
primary key field will automatically be added to your model if you don't specify
|
primary key field will automatically be added to your model if you don't specify
|
||||||
otherwise. See :ref:`automatic-primary-key-fields`.
|
otherwise. See :ref:`automatic-primary-key-fields`.
|
||||||
|
|
||||||
|
``BigIntegerField``
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
.. versionadded:: 1.2
|
||||||
|
|
||||||
|
.. class:: BigIntegerField([**options])
|
||||||
|
|
||||||
|
A 64 bit integer, much like an :class:`IntegerField` except that it is
|
||||||
|
guaranteed to fit numbers from -9223372036854775808 to 9223372036854775807. The
|
||||||
|
admin represents this as an ``<input type="text">`` (a single-line input).
|
||||||
|
|
||||||
|
|
||||||
``BooleanField``
|
``BooleanField``
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,10 @@ the full list of conversions:
|
||||||
=============================== ========================================
|
=============================== ========================================
|
||||||
``AutoField`` Not represented in the form
|
``AutoField`` Not represented in the form
|
||||||
|
|
||||||
|
``BigIntegerField`` ``IntegerField`` with ``min_value`` set
|
||||||
|
to -9223372036854775808 and ``max_value``
|
||||||
|
set to 9223372036854775807.
|
||||||
|
|
||||||
``BooleanField`` ``BooleanField``
|
``BooleanField`` ``BooleanField``
|
||||||
|
|
||||||
``CharField`` ``CharField`` with ``max_length`` set to
|
``CharField`` ``CharField`` with ``max_length`` set to
|
||||||
|
@ -108,6 +112,10 @@ the full list of conversions:
|
||||||
The ``FloatField`` form field and ``DecimalField`` model and form fields
|
The ``FloatField`` form field and ``DecimalField`` model and form fields
|
||||||
are new in Django 1.0.
|
are new in Django 1.0.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2
|
||||||
|
The ``BigIntegerField`` is new in Django 1.2.
|
||||||
|
|
||||||
|
|
||||||
As you might expect, the ``ForeignKey`` and ``ManyToManyField`` model field
|
As you might expect, the ``ForeignKey`` and ``ManyToManyField`` model field
|
||||||
types are special cases:
|
types are special cases:
|
||||||
|
|
||||||
|
|
|
@ -13,12 +13,6 @@ import tempfile
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core.files.storage import FileSystemStorage
|
from django.core.files.storage import FileSystemStorage
|
||||||
|
|
||||||
# Python 2.3 doesn't have sorted()
|
|
||||||
try:
|
|
||||||
sorted
|
|
||||||
except NameError:
|
|
||||||
from django.utils.itercompat import sorted
|
|
||||||
|
|
||||||
temp_storage_dir = tempfile.mkdtemp()
|
temp_storage_dir = tempfile.mkdtemp()
|
||||||
temp_storage = FileSystemStorage(temp_storage_dir)
|
temp_storage = FileSystemStorage(temp_storage_dir)
|
||||||
|
|
||||||
|
@ -201,6 +195,12 @@ class Post(models.Model):
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
class BigInt(models.Model):
|
||||||
|
biggie = models.BigIntegerField()
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return unicode(self.biggie)
|
||||||
|
|
||||||
__test__ = {'API_TESTS': """
|
__test__ = {'API_TESTS': """
|
||||||
>>> from django import forms
|
>>> from django import forms
|
||||||
>>> from django.forms.models import ModelForm, model_to_dict
|
>>> from django.forms.models import ModelForm, model_to_dict
|
||||||
|
@ -1145,6 +1145,28 @@ True
|
||||||
# Delete the current file since this is not done by Django.
|
# Delete the current file since this is not done by Django.
|
||||||
>>> instance.file.delete()
|
>>> instance.file.delete()
|
||||||
>>> instance.delete()
|
>>> instance.delete()
|
||||||
|
|
||||||
|
# BigIntegerField ################################################################
|
||||||
|
>>> class BigIntForm(forms.ModelForm):
|
||||||
|
... class Meta:
|
||||||
|
... model = BigInt
|
||||||
|
...
|
||||||
|
>>> bif = BigIntForm({'biggie': '-9223372036854775808'})
|
||||||
|
>>> bif.is_valid()
|
||||||
|
True
|
||||||
|
>>> bif = BigIntForm({'biggie': '-9223372036854775809'})
|
||||||
|
>>> bif.is_valid()
|
||||||
|
False
|
||||||
|
>>> bif.errors
|
||||||
|
{'biggie': [u'Ensure this value is greater than or equal to -9223372036854775808.']}
|
||||||
|
>>> bif = BigIntForm({'biggie': '9223372036854775807'})
|
||||||
|
>>> bif.is_valid()
|
||||||
|
True
|
||||||
|
>>> bif = BigIntForm({'biggie': '9223372036854775808'})
|
||||||
|
>>> bif.is_valid()
|
||||||
|
False
|
||||||
|
>>> bif.errors
|
||||||
|
{'biggie': [u'Ensure this value is less than or equal to 9223372036854775807.']}
|
||||||
"""}
|
"""}
|
||||||
|
|
||||||
if test_images:
|
if test_images:
|
||||||
|
|
|
@ -4,6 +4,7 @@ class Reporter(models.Model):
|
||||||
first_name = models.CharField(max_length=30)
|
first_name = models.CharField(max_length=30)
|
||||||
last_name = models.CharField(max_length=30)
|
last_name = models.CharField(max_length=30)
|
||||||
email = models.EmailField()
|
email = models.EmailField()
|
||||||
|
facebook_user_id = models.BigIntegerField()
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return u"%s %s" % (self.first_name, self.last_name)
|
return u"%s %s" % (self.first_name, self.last_name)
|
||||||
|
|
|
@ -77,7 +77,7 @@ class IntrospectionTests(TestCase):
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
desc = connection.introspection.get_table_description(cursor, Reporter._meta.db_table)
|
desc = connection.introspection.get_table_description(cursor, Reporter._meta.db_table)
|
||||||
self.assertEqual([datatype(r[1], r) for r in desc],
|
self.assertEqual([datatype(r[1], r) for r in desc],
|
||||||
['IntegerField', 'CharField', 'CharField', 'CharField'])
|
['IntegerField', 'CharField', 'CharField', 'CharField', 'BigIntegerField'])
|
||||||
|
|
||||||
# Regression test for #9991 - 'real' types in postgres
|
# Regression test for #9991 - 'real' types in postgres
|
||||||
if settings.DATABASE_ENGINE.startswith('postgresql'):
|
if settings.DATABASE_ENGINE.startswith('postgresql'):
|
||||||
|
|
|
@ -51,6 +51,9 @@ class BigD(models.Model):
|
||||||
class BigS(models.Model):
|
class BigS(models.Model):
|
||||||
s = models.SlugField(max_length=255)
|
s = models.SlugField(max_length=255)
|
||||||
|
|
||||||
|
class BigInt(models.Model):
|
||||||
|
value = models.BigIntegerField()
|
||||||
|
null_value = models.BigIntegerField(null = True, blank = True)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# ImageField
|
# ImageField
|
||||||
|
|
|
@ -6,7 +6,7 @@ from django import forms
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
from models import Foo, Bar, Whiz, BigD, BigS, Image
|
from models import Foo, Bar, Whiz, BigD, BigS, Image, BigInt
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
@ -144,3 +144,32 @@ class SlugFieldTests(django.test.TestCase):
|
||||||
bs = BigS.objects.create(s = 'slug'*50)
|
bs = BigS.objects.create(s = 'slug'*50)
|
||||||
bs = BigS.objects.get(pk=bs.pk)
|
bs = BigS.objects.get(pk=bs.pk)
|
||||||
self.assertEqual(bs.s, 'slug'*50)
|
self.assertEqual(bs.s, 'slug'*50)
|
||||||
|
|
||||||
|
class BigIntegerFieldTests(django.test.TestCase):
|
||||||
|
def test_limits(self):
|
||||||
|
# Ensure that values that are right at the limits can be saved
|
||||||
|
# and then retrieved without corruption.
|
||||||
|
maxval = 9223372036854775807
|
||||||
|
minval = -maxval - 1
|
||||||
|
BigInt.objects.create(value=maxval)
|
||||||
|
qs = BigInt.objects.filter(value__gte=maxval)
|
||||||
|
self.assertEqual(qs.count(), 1)
|
||||||
|
self.assertEqual(qs[0].value, maxval)
|
||||||
|
BigInt.objects.create(value=minval)
|
||||||
|
qs = BigInt.objects.filter(value__lte=minval)
|
||||||
|
self.assertEqual(qs.count(), 1)
|
||||||
|
self.assertEqual(qs[0].value, minval)
|
||||||
|
|
||||||
|
def test_types(self):
|
||||||
|
b = BigInt(value = 0)
|
||||||
|
self.assertTrue(isinstance(b.value, (int, long)))
|
||||||
|
b.save()
|
||||||
|
self.assertTrue(isinstance(b.value, (int, long)))
|
||||||
|
b = BigInt.objects.all()[0]
|
||||||
|
self.assertTrue(isinstance(b.value, (int, long)))
|
||||||
|
|
||||||
|
def test_coercing(self):
|
||||||
|
BigInt.objects.create(value ='10')
|
||||||
|
b = BigInt.objects.get(value = '10')
|
||||||
|
self.assertEqual(b.value, 10)
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,9 @@ class FloatData(models.Model):
|
||||||
class IntegerData(models.Model):
|
class IntegerData(models.Model):
|
||||||
data = models.IntegerField(null=True)
|
data = models.IntegerField(null=True)
|
||||||
|
|
||||||
|
class BigIntegerData(models.Model):
|
||||||
|
data = models.BigIntegerField(null=True)
|
||||||
|
|
||||||
# class ImageData(models.Model):
|
# class ImageData(models.Model):
|
||||||
# data = models.ImageField(null=True)
|
# data = models.ImageField(null=True)
|
||||||
|
|
||||||
|
|
|
@ -321,6 +321,11 @@ The end."""),
|
||||||
(inherited_obj, 900, InheritAbstractModel, {'child_data':37,'parent_data':42}),
|
(inherited_obj, 900, InheritAbstractModel, {'child_data':37,'parent_data':42}),
|
||||||
(inherited_obj, 910, ExplicitInheritBaseModel, {'child_data':37,'parent_data':42}),
|
(inherited_obj, 910, ExplicitInheritBaseModel, {'child_data':37,'parent_data':42}),
|
||||||
(inherited_obj, 920, InheritBaseModel, {'child_data':37,'parent_data':42}),
|
(inherited_obj, 920, InheritBaseModel, {'child_data':37,'parent_data':42}),
|
||||||
|
|
||||||
|
(data_obj, 1000, BigIntegerData, 9223372036854775807),
|
||||||
|
(data_obj, 1001, BigIntegerData, -9223372036854775808),
|
||||||
|
(data_obj, 1002, BigIntegerData, 0),
|
||||||
|
(data_obj, 1003, BigIntegerData, None),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Because Oracle treats the empty string as NULL, Oracle is expected to fail
|
# Because Oracle treats the empty string as NULL, Oracle is expected to fail
|
||||||
|
|
Loading…
Reference in New Issue