Opened 4 weeks ago
Last modified 4 weeks ago
#37034 assigned Bug
Improve writing migrations how-to add through field on a ManyToManyField
| Reported by: | Clifford Gama | Owned by: | Clifford Gama |
|---|---|---|---|
| Component: | Documentation | Version: | dev |
| Severity: | Normal | Keywords: | migrations, ManyToManyField, through, SeparateDatabaseAndState |
| Cc: | Adam Johnson | Triage Stage: | Accepted |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
- 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. In other words, future model states will not bear a correct representation of what's in the db.
- 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.
Incidentally for the case where db_table remains the same, #36803 may allow us to suggest a simpler alternative in the docs, one that does not need to use SeparateDatabaseAndState.
Change History (7)
comment:1 by , 4 weeks ago
| Cc: | added |
|---|
comment:2 by , 4 weeks ago
As a compromise between demonstrating how to use SeparateDatabaseAndState and demonstrating how to migrate a m2m field's through attribute correctly, I intend to keep the example and mention the alternatives as part of the example. I will show open a PR in a day or so to show what that'd look like.
comment:3 by , 4 weeks ago
| Keywords: | SeparateDatabaseAndState added |
|---|---|
| Triage Stage: | Unreviewed → Accepted |
Thank you for the ticket, Clifford. I agree that a more focused how-to is the right direction. To keep each topic clear, I suggest splitting this into two separate how-to guides: one for SeparateDatabaseAndState, and another for migrating a m2m to use a through model.
These are distinct goals, and combining them makes the example harder to follow and less accurate. This aligns better with the Diataxis definition of how-to guides as goal-oriented instructions:
How-to guides are directions that guide the reader through a problem or towards a result. How-to guides are goal-oriented.
Concretely, one guide would show a clean, representative use of SeparateDatabaseAndState (maybe creating an index on a big table without downtime? is there a DB primitive that Django does not support?), and the other would demonstrate the correct way to migrate an M2M to a through model without relying on it. Even if implemented in a single PR, keeping the guides separate would improve clarity and avoid conflating the two concerns.
comment:4 by , 4 weeks ago
| Has patch: | set |
|---|---|
| Patch needs improvement: | set |
Marking as PNI as I work on fixing the inaccuracies in the ManytoManyField.through section.
comment:5 by , 4 weeks ago
| Patch needs improvement: | unset |
|---|
comment:6 by , 4 weeks ago
| Patch needs improvement: | set |
|---|
comment:7 by , 4 weeks ago
| Patch needs improvement: | unset |
|---|
So from my understanding, this section of the docs was added to add an example of using
SeperateDatabaseAndStaterather than showing the best way to change aManyToManyFieldto use athroughmodel. See https://github.com/django/django/pull/12523 for details.So I suppose my question would be, do you have a better example for using
SeperateDatabaseAndState?