Skip to content

Commit fbdd36f

Browse files
committed
implement SchemaEditor.add/remove_constraint() and creating uniques in create_model()
1 parent 78239f9 commit fbdd36f

File tree

3 files changed

+29
-33
lines changed

3 files changed

+29
-33
lines changed

django_mongodb/features.py

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ class DatabaseFeatures(BaseDatabaseFeatures):
2121
# BSON Date type doesn't support microsecond precision.
2222
supports_microsecond_precision = False
2323
supports_paramstyle_pyformat = False
24+
# Not implemented.
25+
supports_partial_indexes = False
2426
supports_select_difference = False
2527
supports_select_intersection = False
2628
supports_sequence_reset = False
@@ -92,9 +94,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
9294
"schema.tests.SchemaTests.test_alter_field_add_index_to_integerfield",
9395
"schema.tests.SchemaTests.test_alter_field_default_dropped",
9496
"schema.tests.SchemaTests.test_alter_field_fk_to_o2o",
95-
"schema.tests.SchemaTests.test_alter_field_o2o_keeps_unique",
9697
"schema.tests.SchemaTests.test_alter_field_o2o_to_fk",
97-
"schema.tests.SchemaTests.test_alter_int_pk_to_int_unique",
9898
"schema.tests.SchemaTests.test_alter_null_to_not_null",
9999
"schema.tests.SchemaTests.test_alter_null_to_not_null_keeping_default",
100100
"schema.tests.SchemaTests.test_alter_primary_key_the_same_name",
@@ -121,12 +121,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
121121
"schema.tests.SchemaTests.test_m2m_through_alter_custom",
122122
"schema.tests.SchemaTests.test_m2m_through_alter_inherited",
123123
"schema.tests.SchemaTests.test_m2m_through_remove",
124-
# add/remove_constraint
125-
"schema.tests.SchemaTests.test_composed_constraint_with_fk",
126-
"schema.tests.SchemaTests.test_remove_ignored_unique_constraint_not_create_fk_index",
127-
"schema.tests.SchemaTests.test_unique_constraint",
128-
# constraints not fully implemented.
129-
"introspection.tests.IntrospectionTests.test_get_constraints",
130124
}
131125
# $bitAnd, #bitOr, and $bitXor are new in MongoDB 6.3.
132126
_django_test_expected_failures_bitwise = {
@@ -196,24 +190,6 @@ def django_test_expected_failures(self):
196190
"model_fields.test_autofield.SmallAutoFieldTests",
197191
"queries.tests.TestInvalidValuesRelation.test_invalid_values",
198192
},
199-
"MongoDB does not enforce UNIQUE constraints.": {
200-
"auth_tests.test_basic.BasicTestCase.test_unicode_username",
201-
"auth_tests.test_migrations.ProxyModelWithSameAppLabelTests.test_migrate_with_existing_target_permission",
202-
"constraints.tests.UniqueConstraintTests.test_database_constraint",
203-
"contenttypes_tests.test_operations.ContentTypeOperationsTests.test_content_type_rename_conflict",
204-
"contenttypes_tests.test_operations.ContentTypeOperationsTests.test_existing_content_type_rename",
205-
"custom_pk.tests.CustomPKTests.test_unique_pk",
206-
"force_insert_update.tests.ForceInsertInheritanceTests.test_force_insert_with_existing_grandparent",
207-
"get_or_create.tests.GetOrCreateTestsWithManualPKs.test_create_with_duplicate_primary_key",
208-
"get_or_create.tests.GetOrCreateTestsWithManualPKs.test_savepoint_rollback",
209-
"get_or_create.tests.GetOrCreateThroughManyToMany.test_something",
210-
"get_or_create.tests.UpdateOrCreateTests.test_manual_primary_key_test",
211-
"get_or_create.tests.UpdateOrCreateTestsWithManualPKs.test_create_with_duplicate_primary_key",
212-
"introspection.tests.IntrospectionTests.test_get_constraints_unique_indexes_orders",
213-
"model_fields.test_filefield.FileFieldTests.test_unique_when_same_filename",
214-
"one_to_one.tests.OneToOneTests.test_multiple_o2o",
215-
"queries.test_bulk_update.BulkUpdateTests.test_database_routing_batch_atomicity",
216-
},
217193
"MongoDB does not enforce PositiveIntegerField constraint.": {
218194
"model_fields.test_integerfield.PositiveIntegerFieldTests.test_negative_values",
219195
},

django_mongodb/query.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,17 @@
99
from django.db.models.sql.constants import INNER
1010
from django.db.models.sql.datastructures import Join
1111
from django.db.models.sql.where import AND, OR, XOR, NothingNode, WhereNode
12-
from pymongo.errors import DuplicateKeyError, PyMongoError
12+
from pymongo.errors import BulkWriteError, DuplicateKeyError, PyMongoError
1313

1414

1515
def wrap_database_errors(func):
1616
@wraps(func)
1717
def wrapper(*args, **kwargs):
1818
try:
1919
return func(*args, **kwargs)
20+
except BulkWriteError as e:
21+
if "E11000 duplicate key error" in str(e):
22+
raise IntegrityError from e
2023
except DuplicateKeyError as e:
2124
raise IntegrityError from e
2225
except PyMongoError as e:

django_mongodb/schema.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
2-
from django.db.models import Index
2+
from django.db.models import Index, UniqueConstraint
33
from pymongo.operations import IndexModel
44

55

@@ -14,8 +14,8 @@ def create_model(self, model):
1414

1515
def _create_model_indexes(self, model):
1616
"""
17-
Create all indexes (field indexes, index_together, Meta.indexes) for
18-
the specified model.
17+
Create all indexes (field indexes & uniques, index_together,
18+
Meta.constraints, Meta.indexes) for the specified model.
1919
"""
2020
if not model._meta.managed or model._meta.proxy or model._meta.swapped:
2121
return
@@ -25,11 +25,19 @@ def _create_model_indexes(self, model):
2525
index = Index(fields=[field.name])
2626
index.set_name_with_model(model)
2727
self.add_index(model, index)
28+
# Field uniques
29+
for field in model._meta.local_fields:
30+
if field.unique and field.column != "_id":
31+
constraint = UniqueConstraint(fields=[field.name], name="%s_uniq" % field.column)
32+
self.add_constraint(model, constraint)
2833
# Meta.index_together (RemovedInDjango51Warning)
2934
for field_names in model._meta.index_together:
3035
index = Index(fields=field_names)
3136
index.set_name_with_model(model)
3237
self.add_index(model, index)
38+
# Meta.constraints
39+
for constraint in model._meta.constraints:
40+
self.add_constraint(model, constraint)
3341
# Meta.indexes
3442
for index in model._meta.indexes:
3543
self.add_index(model, index)
@@ -81,7 +89,7 @@ def alter_index_together(self, model, old_index_together, new_index_together):
8189
def alter_unique_together(self, model, old_unique_together, new_unique_together):
8290
pass
8391

84-
def add_index(self, model, index):
92+
def add_index(self, model, index, unique=False):
8593
if index.contains_expressions:
8694
return
8795
idx = IndexModel(
@@ -90,6 +98,7 @@ def add_index(self, model, index):
9098
for field_name, order in index.fields_orders
9199
],
92100
name=index.name,
101+
unique=unique,
93102
)
94103
self.connection.database[model._meta.db_table].create_indexes([idx])
95104

@@ -99,10 +108,18 @@ def remove_index(self, model, index):
99108
self.connection.database[model._meta.db_table].drop_index(index.name)
100109

101110
def add_constraint(self, model, constraint):
102-
pass
111+
if constraint.expressions:
112+
return
113+
if isinstance(constraint, UniqueConstraint):
114+
idx = Index(fields=constraint.fields, name=constraint.name)
115+
self.add_index(model, idx, unique=True)
103116

104117
def remove_constraint(self, model, constraint):
105-
pass
118+
if constraint.expressions:
119+
return
120+
if isinstance(constraint, UniqueConstraint):
121+
idx = Index(fields=constraint.fields, name=constraint.name)
122+
self.remove_index(model, idx)
106123

107124
def alter_db_table(self, model, old_db_table, new_db_table):
108125
self.connection.database[old_db_table].rename(new_db_table)

0 commit comments

Comments
 (0)