﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
37281	Converting a unique_together field to a ForeignKey generates a migration with AlterUniqueTogether before AddField (FieldDoesNotExist)	Konstantinos Giannopoulos		"When an existing field that participates in unique_together is replaced by a ForeignKey, and unique_together is updated to reference the new field, makemigrations emits the operations in an unapplyable order: AlterUniqueTogether is placed before the AddField that creates the referenced column.

Steps to reproduce.

Initial models:

{{{
#!python
class A(models.Model):
    pass

class B(models.Model):
    type = models.CharField(max_length=20)
    version = models.IntegerField()

    class Meta:
        unique_together = [(""type"", ""version"")]
}}}

Change B to replace type with a FK:

{{{
#!python
class B(models.Model):
    a = models.ForeignKey(A, on_delete=models.CASCADE, null=True)
    version = models.IntegerField()

    class Meta:
        unique_together = [(""a"", ""version"")]
}}}

makemigrations produces:

{{{
#!python
operations = [
    migrations.AlterUniqueTogether(name=""b"", unique_together={(""a"", ""version"")}),
    migrations.AddField(model_name=""b"", name=""a"", field=models.ForeignKey(...)),
    migrations.RemoveField(model_name=""b"", name=""type""),
]
}}}

migrate then raises:

{{{
django.core.exceptions.FieldDoesNotExist: B has no field named 'a'
}}}

Manually moving AddField before AlterUniqueTogether produces a working migration.

Affected versions.

Confirmed broken on 5.0.9, 5.1.4, 5.2.5, and 6.1. Correct on 3.2.25 and 4.2.16.

Regression.

Bisected between 4.2 (good) and 5.0 (bad). First bad commit:

1282b5e4207440af659ef0e0e0c486fdfba8e7b7 — ""Fixed #32528 -- Replaced django.utils.topological_sort with graphlib.TopologicalSort()"".

That change is a refactor and both sorts are correct; they differ in tie-breaking. graphlib.TopologicalSorter returns nodes that have no ordering constraint between them in insertion order, which differs from the previous implementation and exposes a pre-existing missing dependency (below). The commit is the trigger rather than the underlying defect.

Root cause.

In MigrationAutodetector._get_altered_foo_together_operations, the generated AlterUniqueTogether gains a dependency only when a referenced field is a ForeignKey, and that dependency targets the CREATE of the related model, not the AddField of the local column. Nothing records that AlterUniqueTogether must run after the AddField of the fields it references. Without that edge, _sort_migrations() may order the two operations either way; the previous sort happened to place AddField first, graphlib does not.

Suggested fix.

Add a dependency on the CREATE of every field named in the constraint, not only foreign keys (Type.CREATE matches AddField in check_dependency). This mirrors what generate_created_models() already does for the new-model case:

{{{
#!python
for foo_togethers in new_value:
    for field_name in foo_togethers:
        field = new_model_state.get_field(field_name)
        if field.remote_field and field.remote_field.model:
            dependencies.extend(self._get_dependencies_for_foreign_key(...))
        dependencies.append(
            OperationDependency(
                app_label, model_name, field_name,
                OperationDependency.Type.CREATE,
            )
        )
}}}

With this applied, the operations are ordered AddField -> AlterUniqueTogether -> RemoveField and the migration applies cleanly (verified against 5.2.5 and 6.1).

Related tickets.

A recurring family of ordering bugs affects this operation against field add/remove, all surfacing as FieldDoesNotExist: #23794, #26180, #27933, #28366, #29124. Those concern AlterUniqueTogether vs RemoveField; this ticket is the AddField variant (field-to-FK conversion). It is also distinct from #25551 (new model + FK + unique_together in one migration, fixed in 1.9).

I would like to work on the fix for this if the ticket is accepted. I have a reproduction and a candidate patch ready and am happy to submit a PR with a regression test."	Bug	new	Migrations	6.1	Normal		autodetector unique_together migrations	Konstantinos Giannopoulos	Unreviewed	0	0	0	0	0	0
