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
- 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.
- I think the section should mention that
Field.db_defaultavoids 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.
- No mention of performant alternatives for large tables: The
RunPythonexample iterates row by row with individual saves. For large tables this will be very slow. The docs should note thatQuerySet.bulk_update()or RunSQL are worth considering in that case.
Changing a ManyToManyField to use a through model
- 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 existingManyToManyField.
- The through model example does not accurately reflect the database: The current example uses
on_delete=DO_NOTHINGandmodels.UniqueConstraint, whereas Django's auto-generated through tables useCASCADEandunique_together. Since the state and database are not in sync, I think this could cause issues in later migrations.
- The example can be simplified by setting
Meta.db_tableon the new through model to match the existing table name, eliminating the need for aRunSQLrename 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.
Note:
See TracTickets
for help on using tickets.