Skip to content

Commit ae8bf5a

Browse files
committed
add system check to prohibit AutoField
1 parent 1e16ec6 commit ae8bf5a

File tree

7 files changed

+103
-0
lines changed

7 files changed

+103
-0
lines changed

django_mongodb/base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from .query_utils import regex_match
1414
from .schema import DatabaseSchemaEditor
1515
from .utils import OperationDebugWrapper
16+
from .validation import DatabaseValidation
1617

1718

1819
class Cursor:
@@ -128,6 +129,7 @@ def _isnull_operator(a, b):
128129
features_class = DatabaseFeatures
129130
introspection_class = DatabaseIntrospection
130131
ops_class = DatabaseOperations
132+
validation_class = DatabaseValidation
131133

132134
def get_collection(self, name, **kwargs):
133135
collection = Collection(self.database, name, **kwargs)

django_mongodb/fields/auto.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ def get_prep_value(self, value):
3939
return int(value)
4040
raise ValueError(f"Field '{self.name}' expected an ObjectId but got {value!r}.") from e
4141

42+
def get_internal_type(self):
43+
return "ObjectIdAutoField"
44+
4245
def db_type(self, connection):
4346
return "objectId"
4447

django_mongodb/operations.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,17 @@ def explain_query_prefix(self, format=None, **options):
214214
super().explain_query_prefix(format, **options)
215215
return validated_options
216216

217+
def integer_field_range(self, internal_type):
218+
# MongODB doesn't enforce any integer constraints, but it supports
219+
# integers up to 64 bits.
220+
if internal_type in [
221+
"PositiveBigIntegerField",
222+
"PositiveIntegerField",
223+
"PositiveSmallIntegerField",
224+
]:
225+
return (0, 9223372036854775807)
226+
return (-9223372036854775808, 9223372036854775807)
227+
217228
def prepare_join_on_clause(self, lhs_table, lhs_field, rhs_table, rhs_field):
218229
lhs_expr, rhs_expr = super().prepare_join_on_clause(
219230
lhs_table, lhs_field, rhs_table, rhs_field

django_mongodb/validation.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from django.core import checks
2+
from django.db.backends.base.validation import BaseDatabaseValidation
3+
4+
5+
class DatabaseValidation(BaseDatabaseValidation):
6+
prohibited_fields = {"AutoField", "BigAutoField", "SmallAutoField"}
7+
8+
def check_field_type(self, field, field_type):
9+
"""Prohibit AutoField on MongoDB."""
10+
errors = []
11+
if field.get_internal_type() in self.prohibited_fields:
12+
errors.append(
13+
checks.Error(
14+
f"{self.connection.display_name} does not support {field.__class__.__name__}.",
15+
obj=field,
16+
hint="Use django_mongodb.fields.ObjectIdAutoField instead.",
17+
id="mongodb.E001",
18+
)
19+
)
20+
return errors

tests/invalid_models_tests_/__init__.py

Whitespace-only changes.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from django.core.checks import Error
2+
from django.db import connection, models
3+
from django.test import SimpleTestCase
4+
from django.test.utils import isolate_apps
5+
6+
from django_mongodb.validation import DatabaseValidation
7+
8+
9+
@isolate_apps("invalid_models_tests")
10+
class AutoFieldTests(SimpleTestCase):
11+
def test_autofield_prohibited(self):
12+
class Model(models.Model):
13+
id = models.AutoField(primary_key=True)
14+
15+
field = Model._meta.get_field("id")
16+
validator = DatabaseValidation(connection=connection)
17+
self.assertEqual(
18+
validator.check_field(field),
19+
[
20+
Error(
21+
"MongoDB does not support AutoField.",
22+
hint="Use django_mongodb.fields.ObjectIdAutoField instead.",
23+
obj=field,
24+
id="mongodb.E001",
25+
)
26+
],
27+
)
28+
29+
def test_bigautofield_prohibited(self):
30+
class Model(models.Model):
31+
id = models.BigAutoField(primary_key=True)
32+
33+
field = Model._meta.get_field("id")
34+
validator = DatabaseValidation(connection=connection)
35+
self.assertEqual(
36+
validator.check_field(field),
37+
[
38+
Error(
39+
"MongoDB does not support BigAutoField.",
40+
hint="Use django_mongodb.fields.ObjectIdAutoField instead.",
41+
obj=field,
42+
id="mongodb.E001",
43+
)
44+
],
45+
)
46+
47+
def test_smallautofield_prohibited(self):
48+
class Model(models.Model):
49+
id = models.SmallAutoField(primary_key=True)
50+
51+
field = Model._meta.get_field("id")
52+
validator = DatabaseValidation(connection=connection)
53+
self.assertEqual(
54+
validator.check_field(field),
55+
[
56+
Error(
57+
"MongoDB does not support SmallAutoField.",
58+
hint="Use django_mongodb.fields.ObjectIdAutoField instead.",
59+
obj=field,
60+
id="mongodb.E001",
61+
)
62+
],
63+
)

tests/model_fields_/test_autofield.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ def test_deconstruct(self):
1111
self.assertEqual(args, [])
1212
self.assertEqual(kwargs, {"primary_key": True})
1313

14+
def test_get_internal_type(self):
15+
f = ObjectIdAutoField()
16+
self.assertEqual(f.get_internal_type(), "ObjectIdAutoField")
17+
1418
def test_to_python(self):
1519
f = ObjectIdAutoField()
1620
self.assertEqual(f.to_python("1"), 1)

0 commit comments

Comments
 (0)