|
@@ -318,6 +318,92 @@ could either do nothing (as in the example above) or remove some or all of the
|
|
|
data from the new application. Adjust the second argument of the
|
|
|
:mod:`~django.db.migrations.operations.RunPython` operation accordingly.
|
|
|
|
|
|
+.. _changing-a-manytomanyfield-to-use-a-through-model:
|
|
|
+
|
|
|
+Changing a ``ManyToManyField`` to use a ``through`` model
|
|
|
+=========================================================
|
|
|
+
|
|
|
+If you change a :class:`~django.db.models.ManyToManyField` to use a ``through``
|
|
|
+model, the default migration will delete the existing table and create a new
|
|
|
+one, losing the existing relations. To avoid this, you can use
|
|
|
+:class:`.SeparateDatabaseAndState` to rename the existing table to the new
|
|
|
+table name whilst telling the migration autodetector that the new model has
|
|
|
+been created. You can check the existing table name through
|
|
|
+:djadmin:`sqlmigrate` or :djadmin:`dbshell`. You can check the new table name
|
|
|
+with the through model's ``_meta.db_table`` property. Your new ``through``
|
|
|
+model should use the same names for the ``ForeignKey``\s as Django did. Also if
|
|
|
+it needs any extra fields, they should be added in operations after
|
|
|
+:class:`.SeparateDatabaseAndState`.
|
|
|
+
|
|
|
+For example, if we had a ``Book`` model with a ``ManyToManyField`` linking to
|
|
|
+``Author``, we could add a through model ``AuthorBook`` with a new field
|
|
|
+``is_primary``, like so::
|
|
|
+
|
|
|
+ from django.db import migrations, models
|
|
|
+ import django.db.models.deletion
|
|
|
+
|
|
|
+
|
|
|
+ class Migration(migrations.Migration):
|
|
|
+ dependencies = [
|
|
|
+ ('core', '0001_initial'),
|
|
|
+ ]
|
|
|
+
|
|
|
+ operations = [
|
|
|
+ migrations.SeparateDatabaseAndState(
|
|
|
+ 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',
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ state_operations=[
|
|
|
+ migrations.CreateModel(
|
|
|
+ name='AuthorBook',
|
|
|
+ fields=[
|
|
|
+ (
|
|
|
+ 'id',
|
|
|
+ models.AutoField(
|
|
|
+ auto_created=True,
|
|
|
+ primary_key=True,
|
|
|
+ serialize=False,
|
|
|
+ verbose_name='ID',
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ 'author',
|
|
|
+ models.ForeignKey(
|
|
|
+ on_delete=django.db.models.deletion.DO_NOTHING,
|
|
|
+ to='core.Author',
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ 'book',
|
|
|
+ models.ForeignKey(
|
|
|
+ on_delete=django.db.models.deletion.DO_NOTHING,
|
|
|
+ to='core.Book',
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ migrations.AlterField(
|
|
|
+ model_name='book',
|
|
|
+ name='authors',
|
|
|
+ field=models.ManyToManyField(
|
|
|
+ to='core.Author',
|
|
|
+ through='core.AuthorBook',
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ migrations.AddField(
|
|
|
+ model_name='authorbook',
|
|
|
+ name='is_primary',
|
|
|
+ field=models.BooleanField(default=False),
|
|
|
+ ),
|
|
|
+ ]
|
|
|
+
|
|
|
Changing an unmanaged model to managed
|
|
|
======================================
|
|
|
|