[4.0.x] Fixed #33194 -- Fixed migrations when altering a field with functional indexes/unique constraints on SQLite.
This adjusts Expressions.rename_table_references() to only update alias when needed. Regression in83fcfc9ec8
. Co-authored-by: Simon Charette <charettes@users.noreply.github.com> Backport of86971c4090
from main
This commit is contained in:
parent
f5fd03aebe
commit
00aa3e0b9b
|
@ -212,11 +212,7 @@ class Expressions(TableColumns):
|
||||||
def rename_table_references(self, old_table, new_table):
|
def rename_table_references(self, old_table, new_table):
|
||||||
if self.table != old_table:
|
if self.table != old_table:
|
||||||
return
|
return
|
||||||
expressions = deepcopy(self.expressions)
|
self.expressions = self.expressions.relabeled_clone({old_table: new_table})
|
||||||
self.columns = []
|
|
||||||
for col in self.compiler.query._gen_cols([expressions]):
|
|
||||||
col.alias = new_table
|
|
||||||
self.expressions = expressions
|
|
||||||
super().rename_table_references(old_table, new_table)
|
super().rename_table_references(old_table, new_table)
|
||||||
|
|
||||||
def rename_column_references(self, table, old_column, new_column):
|
def rename_column_references(self, table, old_column, new_column):
|
||||||
|
|
|
@ -10,4 +10,5 @@ Django 3.2.9 fixes several bugs in 3.2.8 and adds compatibility with Python
|
||||||
Bugfixes
|
Bugfixes
|
||||||
========
|
========
|
||||||
|
|
||||||
* ...
|
* Fixed a bug in Django 3.2 that caused a migration crash on SQLite when
|
||||||
|
altering a field with a functional index (:ticket:`33194`).
|
||||||
|
|
|
@ -5,6 +5,7 @@ from django.db.backends.ddl_references import (
|
||||||
from django.db.models import ExpressionList, F
|
from django.db.models import ExpressionList, F
|
||||||
from django.db.models.functions import Upper
|
from django.db.models.functions import Upper
|
||||||
from django.db.models.indexes import IndexExpression
|
from django.db.models.indexes import IndexExpression
|
||||||
|
from django.db.models.sql import Query
|
||||||
from django.test import SimpleTestCase, TransactionTestCase
|
from django.test import SimpleTestCase, TransactionTestCase
|
||||||
|
|
||||||
from .models import Person
|
from .models import Person
|
||||||
|
@ -229,6 +230,27 @@ class ExpressionsTests(TransactionTestCase):
|
||||||
str(self.expressions),
|
str(self.expressions),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_rename_table_references_without_alias(self):
|
||||||
|
compiler = Query(Person, alias_cols=False).get_compiler(connection=connection)
|
||||||
|
table = Person._meta.db_table
|
||||||
|
expressions = Expressions(
|
||||||
|
table=table,
|
||||||
|
expressions=ExpressionList(
|
||||||
|
IndexExpression(Upper('last_name')),
|
||||||
|
IndexExpression(F('first_name')),
|
||||||
|
).resolve_expression(compiler.query),
|
||||||
|
compiler=compiler,
|
||||||
|
quote_value=self.editor.quote_value,
|
||||||
|
)
|
||||||
|
expressions.rename_table_references(table, 'other')
|
||||||
|
self.assertIs(expressions.references_table(table), False)
|
||||||
|
self.assertIs(expressions.references_table('other'), True)
|
||||||
|
expected_str = '(UPPER(%s)), %s' % (
|
||||||
|
self.editor.quote_name('last_name'),
|
||||||
|
self.editor.quote_name('first_name'),
|
||||||
|
)
|
||||||
|
self.assertEqual(str(expressions), expected_str)
|
||||||
|
|
||||||
def test_rename_column_references(self):
|
def test_rename_column_references(self):
|
||||||
table = Person._meta.db_table
|
table = Person._meta.db_table
|
||||||
self.expressions.rename_column_references(table, 'first_name', 'other')
|
self.expressions.rename_column_references(table, 'first_name', 'other')
|
||||||
|
|
|
@ -2106,6 +2106,25 @@ class OperationTests(OperationTestBase):
|
||||||
self.assertEqual(definition[1], [])
|
self.assertEqual(definition[1], [])
|
||||||
self.assertEqual(definition[2], {'model_name': 'Pony', 'name': index_name})
|
self.assertEqual(definition[2], {'model_name': 'Pony', 'name': index_name})
|
||||||
|
|
||||||
|
@skipUnlessDBFeature('supports_expression_indexes')
|
||||||
|
def test_alter_field_with_func_index(self):
|
||||||
|
app_label = 'test_alfuncin'
|
||||||
|
index_name = f'{app_label}_pony_idx'
|
||||||
|
table_name = f'{app_label}_pony'
|
||||||
|
project_state = self.set_up_test_model(
|
||||||
|
app_label,
|
||||||
|
indexes=[models.Index(Abs('pink'), name=index_name)],
|
||||||
|
)
|
||||||
|
operation = migrations.AlterField('Pony', 'pink', models.IntegerField(null=True))
|
||||||
|
new_state = project_state.clone()
|
||||||
|
operation.state_forwards(app_label, new_state)
|
||||||
|
with connection.schema_editor() as editor:
|
||||||
|
operation.database_forwards(app_label, editor, project_state, new_state)
|
||||||
|
self.assertIndexNameExists(table_name, index_name)
|
||||||
|
with connection.schema_editor() as editor:
|
||||||
|
operation.database_backwards(app_label, editor, new_state, project_state)
|
||||||
|
self.assertIndexNameExists(table_name, index_name)
|
||||||
|
|
||||||
def test_alter_field_with_index(self):
|
def test_alter_field_with_index(self):
|
||||||
"""
|
"""
|
||||||
Test AlterField operation with an index to ensure indexes created via
|
Test AlterField operation with an index to ensure indexes created via
|
||||||
|
@ -2664,6 +2683,26 @@ class OperationTests(OperationTestBase):
|
||||||
'name': 'covering_pink_constraint_rm',
|
'name': 'covering_pink_constraint_rm',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def test_alter_field_with_func_unique_constraint(self):
|
||||||
|
app_label = 'test_alfuncuc'
|
||||||
|
constraint_name = f'{app_label}_pony_uq'
|
||||||
|
table_name = f'{app_label}_pony'
|
||||||
|
project_state = self.set_up_test_model(
|
||||||
|
app_label,
|
||||||
|
constraints=[models.UniqueConstraint('pink', 'weight', name=constraint_name)]
|
||||||
|
)
|
||||||
|
operation = migrations.AlterField('Pony', 'pink', models.IntegerField(null=True))
|
||||||
|
new_state = project_state.clone()
|
||||||
|
operation.state_forwards(app_label, new_state)
|
||||||
|
with connection.schema_editor() as editor:
|
||||||
|
operation.database_forwards(app_label, editor, project_state, new_state)
|
||||||
|
if connection.features.supports_expression_indexes:
|
||||||
|
self.assertIndexNameExists(table_name, constraint_name)
|
||||||
|
with connection.schema_editor() as editor:
|
||||||
|
operation.database_backwards(app_label, editor, new_state, project_state)
|
||||||
|
if connection.features.supports_expression_indexes:
|
||||||
|
self.assertIndexNameExists(table_name, constraint_name)
|
||||||
|
|
||||||
def test_add_func_unique_constraint(self):
|
def test_add_func_unique_constraint(self):
|
||||||
app_label = 'test_adfuncuc'
|
app_label = 'test_adfuncuc'
|
||||||
constraint_name = f'{app_label}_pony_abs_uq'
|
constraint_name = f'{app_label}_pony_abs_uq'
|
||||||
|
|
Loading…
Reference in New Issue