Opened 4 hours ago

#37031 assigned Bug

Improve "Writing migrations" how-to -- unique fields and ManyToManyField through models

Reported by: Clifford Gama Owned by: Clifford Gama
Component: Documentation Version: 6.0
Severity: Normal Keywords: migrations
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

In the writing migrations docs there are two advanced migration scenarios that have inaccuracies and could be made clearer.

Migrations that add unique fields

  1. The current approach splits the work across three files: one to add the field, one to populate values, and one to restore the constraint. All three operations can be placed in a single migration, which is simpler to follow and has the added benefit of being atomic — removing the race condition that is currently warned about in the docs.
  1. I think the section should mention that Field.db_default avoids this problem entirely by having the database generate a unique value per row. This is worth noting upfront so readers can choose the simpler path where their use case allows.
  1. No mention of performant alternatives for large tables: The RunPython example iterates row by row with individual saves. For large tables this will be very slow. The docs should note that QuerySet.bulk_update() or RunSQL are worth considering in that case.

Changing a ManyToManyField to use a through model

  1. Inaccurate description of how Django handles this change: The section states that "the default migration will delete the existing table and create a new one". This is not accurate. Django refuses to apply a migration when through= is added/changed on an existing ManyToManyField.
  1. The through model example does not accurately reflect the database: The current example uses on_delete=DO_NOTHING and models.UniqueConstraint, whereas Django's auto-generated through tables use CASCADE and unique_together. Since the state and database are not in sync, I think this could cause issues in later migrations.
  1. The example can be simplified by setting Meta.db_table on the new through model to match the existing table name, eliminating the need for a RunSQL rename operation.

The section also suggests using sqlmigrate or dbshell to find the existing table name, which is indirect. The simplest approach is to inspect field.through._meta.db_table before modifying the field.

Change History (0)

Note: See TracTickets for help on using tickets.
Back to Top