Skip to content

Commit 096096a

Browse files
committed
add unique creation/deletion support to SchemaEditor.alter_field()
1 parent 3e22e60 commit 096096a

File tree

2 files changed

+41
-6
lines changed

2 files changed

+41
-6
lines changed

django_mongodb/features.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
8181
"backends.tests.ThreadTests.test_pass_connection_between_threads",
8282
"backends.tests.ThreadTests.test_closing_non_shared_connections",
8383
"backends.tests.ThreadTests.test_default_connection_thread_local",
84-
# AlterField
85-
"schema.tests.SchemaTests.test_alter_field_fk_to_o2o",
86-
"schema.tests.SchemaTests.test_alter_field_o2o_to_fk",
87-
# AlterField (unique)
88-
"schema.tests.SchemaTests.test_indexes",
89-
"schema.tests.SchemaTests.test_unique",
9084
}
9185
# $bitAnd, #bitOr, and $bitXor are new in MongoDB 6.3.
9286
_django_test_expected_failures_bitwise = {

django_mongodb/schema.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,30 @@ def _alter_field(
109109
strict=False,
110110
):
111111
collection = self.connection.database[model._meta.db_table]
112+
# Has unique been removed?
113+
if old_field.unique and (
114+
not new_field.unique or self._field_became_primary_key(old_field, new_field)
115+
):
116+
# Find the unique constraint for this field
117+
meta_constraint_names = {constraint.name for constraint in model._meta.constraints}
118+
constraint_names = self._constraint_names(
119+
model,
120+
[old_field.column],
121+
unique=True,
122+
primary_key=False,
123+
exclude=meta_constraint_names,
124+
)
125+
if strict and len(constraint_names) != 1:
126+
raise ValueError(
127+
f"Found wrong number ({len(constraint_names)}) of unique "
128+
f"constraints for {model._meta.db_table}.{old_field.column}"
129+
)
130+
for constraint_name in constraint_names:
131+
constraint = UniqueConstraint(
132+
fields=[old_field.name],
133+
name=constraint_name,
134+
)
135+
self.remove_constraint(model, constraint)
112136
# Removed an index? (no strict check, as multiple indexes are possible)
113137
# Remove indexes if db_index switched to False or a unique constraint
114138
# will now be used in lieu of an index. The following lines from the
@@ -134,12 +158,29 @@ def _alter_field(
134158
):
135159
self._drop_index_for_field(model, old_field)
136160
self._add_index_for_field(model, new_field)
161+
# Move unique to the new field, if needed.
162+
if old_field.unique and new_field.unique and new_field.column != "_id":
163+
if not old_field.primary_key:
164+
old_constraint = UniqueConstraint(
165+
fields=[old_field.name], name=f"{model._meta.db_table}_{old_field.column}_key"
166+
)
167+
self.remove_constraint(model, old_constraint)
168+
new_constraint = UniqueConstraint(
169+
fields=[new_field.name], name=f"{model._meta.db_table}_{new_field.column}_key"
170+
)
171+
self.add_constraint(model, new_constraint, field=new_field)
137172
# Replace NULL with the field default if the field and was changed from
138173
# NULL to NOT NULL.
139174
if new_field.has_default() and old_field.null and not new_field.null:
140175
column = new_field.column
141176
default = self.effective_default(new_field)
142177
collection.update_many({column: {"$eq": None}}, [{"$set": {column: default}}])
178+
# Added a unique?
179+
if self._unique_should_be_added(old_field, new_field):
180+
constraint = UniqueConstraint(
181+
fields=[new_field.name], name=f"{model._meta.db_table}_{new_field.column}_key"
182+
)
183+
self.add_constraint(model, constraint, field=new_field)
143184
# Added an index? Add an index if db_index switched to True or a unique
144185
# constraint will no longer be used in lieu of an index. The following
145186
# lines from the truth table show all True cases; the rest are False:

0 commit comments

Comments
 (0)