|
4 | 4 | import binascii |
5 | 5 | import datetime |
6 | 6 |
|
| 7 | +from collections import defaultdict |
| 8 | + |
7 | 9 | from django.db.backends.base.schema import ( |
8 | 10 | BaseDatabaseSchemaEditor, |
9 | 11 | _is_relevant_relation, |
@@ -92,6 +94,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): |
92 | 94 | sql_create_unique_null = "CREATE UNIQUE INDEX %(name)s ON %(table)s(%(columns)s) " \ |
93 | 95 | "WHERE %(columns)s IS NOT NULL" |
94 | 96 |
|
| 97 | + _deferred_unique_indexes = defaultdict(list) |
| 98 | + |
95 | 99 | def _alter_column_default_sql(self, model, old_field, new_field, drop=False): |
96 | 100 | """ |
97 | 101 | Hook to specialize column default alteration. |
@@ -279,6 +283,15 @@ def _db_table_delete_constraint_sql(self, template, db_table, name): |
279 | 283 | include='' |
280 | 284 | ) |
281 | 285 |
|
| 286 | + def _delete_deferred_unique_indexes_for_field(self, field): |
| 287 | + deferred_statements = self._deferred_unique_indexes.get(str(field), []) |
| 288 | + for stmt in deferred_statements: |
| 289 | + if stmt in self.deferred_sql: |
| 290 | + self.deferred_sql.remove(stmt) |
| 291 | + |
| 292 | + def _add_deferred_unique_index_for_field(self, field, statement): |
| 293 | + self._deferred_unique_indexes[str(field)].append(statement) |
| 294 | + |
282 | 295 | def _alter_field(self, model, old_field, new_field, old_type, new_type, |
283 | 296 | old_db_params, new_db_params, strict=False): |
284 | 297 | """Actually perform a "physical" (non-ManyToMany) field update.""" |
@@ -542,6 +555,7 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type, |
542 | 555 | self.execute(self._create_unique_sql(model, [new_field])) |
543 | 556 | else: |
544 | 557 | self.execute(self._create_unique_sql(model, [new_field.column])) |
| 558 | + self._delete_deferred_unique_indexes_for_field(new_field) |
545 | 559 | # Added an index? |
546 | 560 | # constraint will no longer be used in lieu of an index. The following |
547 | 561 | # lines from the truth table show all True cases; the rest are False: |
@@ -574,6 +588,7 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type, |
574 | 588 | self.execute(self._create_unique_sql(model, [old_field])) |
575 | 589 | else: |
576 | 590 | self.execute(self._create_unique_sql(model, columns=[old_field.column])) |
| 591 | + self._delete_deferred_unique_indexes_for_field(old_field) |
577 | 592 | else: |
578 | 593 | if django_version >= (4, 0): |
579 | 594 | for field_names in model._meta.unique_together: |
@@ -846,9 +861,11 @@ def add_field(self, model, field): |
846 | 861 | not field.many_to_many and field.null and field.unique): |
847 | 862 |
|
848 | 863 | definition = definition.replace(' UNIQUE', '') |
849 | | - self.deferred_sql.append(self._create_index_sql( |
| 864 | + statement = self._create_index_sql( |
850 | 865 | model, [field], sql=self.sql_create_unique_null, suffix="_uniq" |
851 | | - )) |
| 866 | + ) |
| 867 | + self.deferred_sql.append(statement) |
| 868 | + self._add_deferred_unique_index_for_field(field, statement) |
852 | 869 |
|
853 | 870 | # Check constraints can go on the column SQL here |
854 | 871 | db_params = field.db_parameters(connection=self.connection) |
@@ -1012,9 +1029,11 @@ def create_model(self, model): |
1012 | 1029 | not field.many_to_many and field.null and field.unique): |
1013 | 1030 |
|
1014 | 1031 | definition = definition.replace(' UNIQUE', '') |
1015 | | - self.deferred_sql.append(self._create_index_sql( |
| 1032 | + statement = self._create_index_sql( |
1016 | 1033 | model, [field], sql=self.sql_create_unique_null, suffix="_uniq" |
1017 | | - )) |
| 1034 | + ) |
| 1035 | + self.deferred_sql.append(statement) |
| 1036 | + self._add_deferred_unique_index_for_field(field, statement) |
1018 | 1037 |
|
1019 | 1038 | # Check constraints can go on the column SQL here |
1020 | 1039 | db_params = field.db_parameters(connection=self.connection) |
|
0 commit comments