The Django documentation has this exact case as an appplication of the migrations.SeparateDatabaseAndState operation. I did exactly what the documentation said but Django kept throwing an exception saying that the table used for the M2M mapping didn't exist.
I was assigning a table name through the "db_table" attribute of the Meta class of the intermediary model and this was causing the problem (don't know why).
I then understood that the SQL code shown in the example in the Django documentation is to change the name assigned by Django to the M2M relationship table in the standard M2M relationship to the new name assigned by Django to the table corresponding to the intermediary model used.
database_operations=[
# Old table name from checking with sqlmigrate, new table
# name from AuthorBook._meta.db_table.
migrations.RunSQL(
sql='ALTER TABLE core_book_authors RENAME TO core_authorbook',
reverse_sql='ALTER TABLE core_authorbook RENAME TO core_book_authors',
),
],
In this example, "core_book_authors" is the old name and "core_authorbook" is the new name of the M2M relationship table. If you do not include this code in the migration, you won't be able to add extra fields to the intermediary model (and I assume this is the main reason to have a custom M2M relationship) as Django will look for the new table name.
To sum up what I did to change a standard M2M relationship to a custom one using 'through':
- Created intermediary model without the extra fields (only the two foreign keys) and specified that the M2M relationship was now to be made via that model (using 'through').
- Run command
py manage.py makemigrations
. I changed this autogenerated migration to look like the one in the documentation referenced above.
- Run command
py manage.py migrate
.
- Added all the extra fields that I needed in the intermediary model.
- Run command
py manage.py makemigrations
.
- Run command
py manage.py migrate
.
The table that previously represented the standard M2M relationship will now have a different name and all the new columns. It is important that these columns have a default value if you already had data in the table.
I find this to be the most straightforward way to do this WITHOUT losing any data.