Skip to content

Restore Meta.indexes after RenameField and AlterField - #584

Open
Rob Berwick (robberwick) wants to merge 2 commits into
microsoft:devfrom
robberwick:rename-alter-meta-indexes
Open

Restore Meta.indexes after RenameField and AlterField#584
Rob Berwick (robberwick) wants to merge 2 commits into
microsoft:devfrom
robberwick:rename-alter-meta-indexes

Conversation

@robberwick

Copy link
Copy Markdown
Contributor

Fixes #499. A migration that renames a field and then changes its type or nullability in the same migration could drop a Meta.indexes index permanently or raise FieldDoesNotExist.

ProjectState.rename_field() updates the model field name but leaves Index.fields with the original name. The schema editor subsequently attempted to resolve that stale name against the renamed model state.

  • Resolves stale index field names to new_field.column while locating indexes to drop.
  • Identifies indexes referencing either the old or renamed column during restoration.
  • Rebuilds affected Index instances with the current field name before generating the CREATE INDEX SQL, preserving index options, explicit names, and ordering.
  • Enables the existing regression coverage for rename-plus-type-change and rename-plus-nullability-change migrations.

…dexes tests

The two tests covering the scenario where RenameField and AlterField (type
change or nullability change) occur in the same migration were previously
marked @expectedfailure because the fix was not yet in place.

Remove the @expectedfailure decorators so that these tests will fail (RED)
until the corresponding fix in schema.py is committed.
…ld in same migration

Django's ProjectState.rename_field() updates model fields but does NOT update
Index.fields in Meta.indexes, leaving stale field names after a rename. When
AlterField then runs in the same migration, _delete_indexes() and the restoration
phase would call model._meta.get_field() with the old name and raise FieldDoesNotExist.

Fix in three locations in schema.py:
- _delete_indexes(): add _resolve_column() helper that catches FieldDoesNotExist
  and falls back to new_field.column; also check new_field.column membership
- _alter_field() collection phase: per-field try/except when building index_columns_list
- _alter_field() restoration phase: reconstruct stale Index objects via deconstruct()
  with corrected field names so Index.__init__ properly derives fields_orders
  (Index.create_sql uses fields_orders, not fields, to resolve field names to columns)
Copilot AI lite review requested due to automatic review settings August 26, 2026 13:49
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a Django migration edge case in the SQL Server backend where Meta.indexes entries could be dropped (or index handling could error) when a RenameField is followed by an AlterField (type/nullability) in the same migration, by making index drop/restore logic resilient to stale field names (issue #499).

Changes:

  • Updates mssql/schema.py to resolve stale Index.fields names when determining which indexes to drop/restore and when generating CREATE INDEX SQL.
  • Rebuilds Index instances with corrected field names before calling create_sql() so explicit names/options/ordering are preserved.
  • Enables the existing regression tests by removing @expectedFailure and updating docstrings to reference #499.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
mssql/schema.py Adjusts index deletion/restoration logic in _alter_field() / _delete_indexes() to handle stale index field names after rename+alter sequences.
testapp/tests/test_indexes.py Turns previously expected-failure scenarios into active regressions for rename+type-change and rename+nullability-change.
Suppressed comments (1)

mssql/schema.py:999

  • The stale-field detection checks model._meta.get_field(field_name) against raw values from index.fields. For ordered indexes, those raw values can start with '-' (e.g. '-a'), which will always raise FieldDoesNotExist even when the underlying field exists, causing unnecessary cloning/reconstruction.
                for field_name in index.fields:
                    try:
                        model._meta.get_field(field_name)
                    except FieldDoesNotExist:
                        has_stale_fields = True

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread mssql/schema.py
Comment on lines +965 to +969
for field_name in index.fields_orders:
try:
index_columns_list.append(model._meta.get_field(field_name).column)
except FieldDoesNotExist:
index_columns_list.append(new_field.column)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Indexes from Meta.indexes not restored when RenameField and AlterField (type or nullability change) occur in the same migration

2 participants