Clarified SeparateDatabaseAndState docs and added example of changing ManyToManyField.
Co-Authored-By: Mariusz Felisiak <felisiak.mariusz@gmail.com> Co-Authored-By: Carlton Gibson <carlton.gibson@noumenal.es> Co-Authored-By: René Fleschenberg <rene@fleschenberg.net>
This commit is contained in:
parent
a2f554249e
commit
a9ee6872bd
|
@ -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
|
data from the new application. Adjust the second argument of the
|
||||||
:mod:`~django.db.migrations.operations.RunPython` operation accordingly.
|
: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
|
Changing an unmanaged model to managed
|
||||||
======================================
|
======================================
|
||||||
|
|
||||||
|
|
|
@ -419,12 +419,24 @@ if ``atomic=True`` is passed to the ``RunPython`` operation.
|
||||||
|
|
||||||
.. class:: SeparateDatabaseAndState(database_operations=None, state_operations=None)
|
.. class:: SeparateDatabaseAndState(database_operations=None, state_operations=None)
|
||||||
|
|
||||||
A highly specialized operation that let you mix and match the database
|
A highly specialized operation that lets you mix and match the database
|
||||||
(schema-changing) and state (autodetector-powering) aspects of operations.
|
(schema-changing) and state (autodetector-powering) aspects of operations.
|
||||||
|
|
||||||
It accepts two lists of operations, and when asked to apply state will use the
|
It accepts two lists of operations. When asked to apply state, it will use the
|
||||||
state list, and when asked to apply changes to the database will use the database
|
``state_operations`` list (this is a generalized version of :class:`RunSQL`'s
|
||||||
list. Do not use this operation unless you're very sure you know what you're doing.
|
``state_operations`` argument). When asked to apply changes to the database, it
|
||||||
|
will use the ``database_operations`` list.
|
||||||
|
|
||||||
|
If the actual state of the database and Django's view of the state get out of
|
||||||
|
sync, this can break the migration framework, even leading to data loss. It's
|
||||||
|
worth exercising caution and checking your database and state operations
|
||||||
|
carefully. You can use :djadmin:`sqlmigrate` and :djadmin:`dbshell` to check
|
||||||
|
your database operations. You can use :djadmin:`makemigrations`, especially
|
||||||
|
with :option:`--dry-run<makemigrations --dry-run>`, to check your state
|
||||||
|
operations.
|
||||||
|
|
||||||
|
For an example using ``SeparateDatabaseAndState``, see
|
||||||
|
:ref:`changing-a-manytomanyfield-to-use-a-through-model`.
|
||||||
|
|
||||||
.. _writing-your-own-migration-operation:
|
.. _writing-your-own-migration-operation:
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue