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

  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. In other words, future model states will not bear a correct representation of what's in the db.
  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.

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 Sarah Boyce, 4 weeks ago

Cc: Adam Johnson added

So from my understanding, this section of the docs was added to add an example of using SeperateDatabaseAndState rather than showing the best way to change a ManyToManyField to use a through model. 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?

comment:2 by Clifford Gama, 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 Natalia Bidart, 4 weeks ago

Keywords: SeparateDatabaseAndState added
Triage Stage: UnreviewedAccepted

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 Clifford Gama, 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 Clifford Gama, 4 weeks ago

Patch needs improvement: unset

comment:6 by JaeHyuckSa, 4 weeks ago

Patch needs improvement: set

comment:7 by Clifford Gama, 4 weeks ago

Patch needs improvement: unset
Note: See TracTickets for help on using tickets.
Back to Top