Skip to content

Commit 5f881bd

Browse files
committed
Fix unique constraints for values > 32 bits on various IntegerFields
1 parent 2066af9 commit 5f881bd

File tree

5 files changed

+103
-5
lines changed

5 files changed

+103
-5
lines changed

django_mongodb_backend/base.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,16 @@ class DatabaseWrapper(BaseDatabaseWrapper):
4646
"FileField": "string",
4747
"FilePathField": "string",
4848
"FloatField": "double",
49-
"IntegerField": "int",
49+
"IntegerField": "long",
5050
"BigIntegerField": "long",
5151
"GenericIPAddressField": "string",
5252
"JSONField": "object",
53-
"PositiveBigIntegerField": "int",
53+
"PositiveBigIntegerField": "long",
5454
"PositiveIntegerField": "long",
55-
"PositiveSmallIntegerField": "int",
55+
"PositiveSmallIntegerField": "long",
5656
"SlugField": "string",
5757
"SmallAutoField": "", # Not supported
58-
"SmallIntegerField": "int",
58+
"SmallIntegerField": "long",
5959
"TextField": "string",
6060
"TimeField": "date",
6161
"UUIDField": "string",

django_mongodb_backend/operations.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import uuid
55
from decimal import Decimal
66

7-
from bson.decimal128 import Decimal128
7+
from bson import Decimal128, Int64
88
from django.conf import settings
99
from django.db import DataError
1010
from django.db.backends.base.operations import BaseDatabaseOperations
@@ -58,6 +58,11 @@ def adapt_decimalfield_value(self, value, max_digits=None, decimal_places=None):
5858
return None
5959
return Decimal128(value)
6060

61+
def adapt_integerfield_value(self, value, internal_type):
62+
if value is None:
63+
return None
64+
return Int64(value)
65+
6166
def adapt_json_value(self, value, encoder):
6267
if encoder is None:
6368
return value

docs/source/releases/5.2.x.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ Bug fixes
3333
- :meth:`QuerySet.explain() <django.db.models.query.QuerySet.explain>` now
3434
:ref:`returns a string that can be parsed as JSON <queryset-explain>`.
3535
- Improved ``QuerySet`` performance by removing low limit on server-side chunking.
36+
- Fixed unique constraint generation for ``SmallIntegerField``, ``IntegerField``,
37+
``PositiveSmallIntegerField``, and ``PositiveBigIntegerField``, which incorrectly
38+
allowed duplicate values larger than 32 bits. All integer field values are now sent
39+
to MongoDB as ``bson.Int64``. Existing unique constraints and data must be corrected
40+
manually.
3641

3742
5.2.0 beta 1
3843
============

tests/model_fields_/models.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@
1313
from django_mongodb_backend.models import EmbeddedModel
1414

1515

16+
class Integers(models.Model):
17+
small = models.SmallIntegerField(unique=True, null=True)
18+
normal = models.IntegerField(unique=True, null=True)
19+
big = models.BigIntegerField(unique=True, null=True)
20+
positive_small = models.PositiveSmallIntegerField(unique=True, null=True)
21+
positive = models.PositiveIntegerField(unique=True, null=True)
22+
positive_big = models.PositiveBigIntegerField(unique=True, null=True)
23+
24+
1625
# ObjectIdField
1726
class ObjectIdModel(models.Model):
1827
field = ObjectIdField()
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from django.db import IntegrityError
2+
from django.test import TestCase
3+
4+
from .models import Integers
5+
6+
7+
class SmallUniqueTests(TestCase):
8+
"""
9+
Duplicate values < 32 bits are prohibited. This confirms integer field values are
10+
cast to Int64 so MongoDB stores it as long. Otherwise, the
11+
partialFilterExpression: {$type: long} unique constraint doesn't work.
12+
"""
13+
14+
def test_smallintegerfield(self):
15+
Integers.objects.create(small=123)
16+
with self.assertRaises(IntegrityError):
17+
Integers.objects.create(small=123)
18+
19+
def test_integerfield(self):
20+
Integers.objects.create(normal=123)
21+
with self.assertRaises(IntegrityError):
22+
Integers.objects.create(normal=123)
23+
24+
def test_bigintegerfield(self):
25+
Integers.objects.create(big=123)
26+
with self.assertRaises(IntegrityError):
27+
Integers.objects.create(big=123)
28+
29+
def test_positivesmallintegerfield(self):
30+
Integers.objects.create(positive_small=123)
31+
with self.assertRaises(IntegrityError):
32+
Integers.objects.create(positive_small=123)
33+
34+
def test_positiveintegerfield(self):
35+
Integers.objects.create(positive=123)
36+
with self.assertRaises(IntegrityError):
37+
Integers.objects.create(positive=123)
38+
39+
def test_positivebigintegerfield(self):
40+
Integers.objects.create(positive_big=123)
41+
with self.assertRaises(IntegrityError):
42+
Integers.objects.create(positive_big=123)
43+
44+
45+
class LargeUniqueTests(TestCase):
46+
"""
47+
Duplicate values > 32 bits are prohibited. This confirms each field uses the long
48+
db_type() rather than the 32 bit int type.
49+
"""
50+
51+
def test_smallintegerfield(self):
52+
Integers.objects.create(small=2**31)
53+
with self.assertRaises(IntegrityError):
54+
Integers.objects.create(small=2**31)
55+
56+
def test_integerfield(self):
57+
Integers.objects.create(normal=2**31)
58+
with self.assertRaises(IntegrityError):
59+
Integers.objects.create(normal=2**31)
60+
61+
def test_bigintegerfield(self):
62+
Integers.objects.create(big=2**31)
63+
with self.assertRaises(IntegrityError):
64+
Integers.objects.create(big=2**31)
65+
66+
def test_positivesmallintegerfield(self):
67+
Integers.objects.create(positive_small=2**31)
68+
with self.assertRaises(IntegrityError):
69+
Integers.objects.create(positive_small=2**31)
70+
71+
def test_positiveintegerfield(self):
72+
Integers.objects.create(positive=2**31)
73+
with self.assertRaises(IntegrityError):
74+
Integers.objects.create(positive=2**31)
75+
76+
def test_positivebigintegerfield(self):
77+
Integers.objects.create(positive_big=2**31)
78+
with self.assertRaises(IntegrityError):
79+
Integers.objects.create(positive_big=2**31)

0 commit comments

Comments
 (0)