From b7ea494d65e4d9703a0a24f0cd708293df88f48b Mon Sep 17 00:00:00 2001 From: rixx Date: Sun, 3 Apr 2016 17:20:35 +0200 Subject: [PATCH] Fixed #24016 -- Added documentation about third-party app data migrations There was confusion about how to migrate data from third-party applications when you are going to uninstall the application later on. Thanks to Markus, Marten and Sergei for help and review. --- AUTHORS | 1 + docs/howto/writing-migrations.txt | 61 +++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/AUTHORS b/AUTHORS index f6bd9191db..537d11212d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -713,6 +713,7 @@ answer newbie questions, and generally made Django that much better: Tim Graham Tim Heap Tim Saylor + Tobias Kunze Tobias McNulty tobias@neuyork.de Todd O'Bryan diff --git a/docs/howto/writing-migrations.txt b/docs/howto/writing-migrations.txt index adef507fa8..6ef6f6e74a 100644 --- a/docs/howto/writing-migrations.txt +++ b/docs/howto/writing-migrations.txt @@ -271,3 +271,64 @@ Prefer using ``dependencies`` over ``run_before`` when possible. You should only use ``run_before`` if it is undesirable or impractical to specify ``dependencies`` in the migration which you want to run after the one you are writing. + +Migrating data when replacing an external app +============================================= + +If you plan to move from one external application to another one with a similar +data structure, you can use a data migration. If you plan to remove the old +application later, you will need to set the ``dependencies`` property +dynamically. Otherwise you will have missing dependencies once you uninstall +the old application. + +.. snippet:: + :filename: myapp/migrations/0124_ensure_dependencies.py + + from django.apps import apps as global_apps + from django.db import migrations + + def forward(apps, schema_editor): + """ + see below + """ + + class Migration(migrations.Migration): + + operations = [ + migrations.RunPython(forward, migrations.RunPython.noop), + ] + dependencies = [ + ('myapp', '0123_the_previous_migration'), + ('new_external_app', '0001_initial'), + ] + + if global_apps.is_installed('old_external_app'): + dependencies.append(('old_external_app', '0001_initial')) + +In your data migration method, you will need to test for the old application +model: + +.. snippet:: + :filename: myapp/migrations/0124_ensure_dependencies.py + + def forward(apps, schema_editor): + try: + OldModel = apps.get_model('old_external', 'OldModel') + except LookupError: + return + + NewModel = apps.get_model('new_external', 'NewModel') + NewModel.objects.bulk_create( + NewModel(new_attribute=old_object.old_attribute) + for old_object in OldModel.objects.all() + ) + +This way you can deploy your application anywhere without first installing +and then uninstalling your old external dependency. If the old external +dependency is not installed when the migration runs it will just do nothing +instead of migrating the data. + +Please take also into consideration what you want to happen when the migration +is unapplied - you could either do nothing or remove some or all data from +the new application model; adjust the second argument of the +:mod:`~django.db.migrations.operations.RunPython` operation accordingly.