Opened 10 years ago

Closed 10 years ago

Last modified 19 months ago

#23347 closed New feature (needsinfo)

Add --dry-run to migrate

Reported by: Richard Eames Owned by: nobody
Component: Migrations Version: 1.7-rc-3
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

In a similar vein to https://code.djangoproject.com/ticket/23263, I would like to propose that a "--dry-run" or "--check" be added to the migrate command. Basically, I want to be able to run through the migrations without it making database changes, so that I can see if I've broken something, especially if I'm writing a migration with a RunPython.

Another way I can see this working, is if sqlmigrate could also output all the sql that was issued, including those issued by RunPython, so that if I had the following migration [0] it would output something similar to [1]

[0] Migration:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations

def update_contenttypes(apps, schema_editor):
        ContentType = apps.get_models('contenttypes', 'ContentType')
        ContentType.objects.get(pk = 1)

class Migration(migrations.Migration):
        dependencies = [
                ('everdeal', '0001_initial'),
        ]
        
        operations = [
                migrations.RunPython(update_contenttypes)
        ]

[1] Output:

BEGIN;
--
-- MIGRATION NOW PERFORMS OPERATION THAT CANNOT BE WRITTEN AS SQL:
-- Raw Python operation
SELECT * FROM contenttypes_contenttype WHERE id=1;
--

ROLLBACK;


Change History (9)

comment:1 by Tim Graham, 10 years ago

Resolution: needsinfo
Status: newclosed

I don't see a way to implement what you propose. I don't think there is a way to output SQL for arbitrary RunPython without actually running it as queries may dependent on each other (for example manipulating models in a loop of MyModel.objects.all()). If you have an idea, please provide it (or a patch).

comment:2 by Richard Eames, 10 years ago

Maybe my understanding of databases is wrong, but couldn't you create a savepoint at the beginning, do all the migrations - including the RunPython - then issue a rollback at the end?

comment:3 by Simon Charette, 10 years ago

@Naddiseo unfortunately not all supported backends support transactional DDL which might end up committing the active transaction silently.

We can't assume only SQL will be executed by RunPython given the provided schema_editor.

comment:4 by Tim Graham, 10 years ago

(Was about to make a similar comment as @charettes.)

As for alternatives: I don't quite understand your use case "so I can see if I've broken something." Migrations are executed when you run tests so you could test it there. Also the migration is executed in a transaction, so if it breaks somewhere in the middle, any changes should be rolledback. Maybe you could elaborate why either of these options aren't sufficient.

comment:5 by Richard Eames, 10 years ago

Also the migration is executed in a transaction, so if it breaks somewhere in the middle, any changes should be rolledback.

Unless it's a backend that doesn't support transactions?

My "use case" is that I like to do incremental testing while I'm developing - and by testing, I don't mean test cases, I mean running the code to see if it doesn't have runtime errors - so if I'm writing a data migration, I may right a small section of it, then run the migration to see if what I have works, then move on to the next part, but I don't want the database to be changed when I run it again. Currently, I can work around this by raising an Exception to force a rollback; providing a "dry-run" option would, for me, be a preferable approach, and providing the would-be committed sql would aid in any kind of debugging where looking at the sql is necessary.

I guess if not all of the backends support transactions, then I guess this can't really be done (cleanly anyway), and this ticket can be resolved. Thanks for your time.

comment:6 by Alexander Schepanovski, 9 years ago

This would be use full even without RunPython support. I used this feature a lot in south.

comment:7 by Markus Holtermann, 9 years ago

If you want to see the particular SQL code generated for a migration you can use sqlmigrate

comment:8 by Craig de Stigter, 6 years ago

I've re-requested a similar thing in #29198

comment:9 by Michael, 19 months ago

The --plan option does not help me, it just says Raw Python operation for my custom migration.

Maybe this will help others: I just added a error at the end of the migration, so that it rolls back, now can run it as many times as desired, then remove the error when confident:

from django.db import migrations


def forwards_func(apps, schema_editor):
    Meeting = apps.get_model("meetings", "Meeting")
    db_alias = schema_editor.connection.alias
    for meeting in Meeting.objects.using(db_alias).all():
        if meeting.meeting_utc is None:
            print(meeting.meeting_date, meeting.meeting_time)
    raise ValueError("Don't commit!")  # <----------------

class Migration(migrations.Migration):
    dependencies = [
        ('meetings', '0004_meeting_meeting_utc_meeting_time_is_null'),
    ]

    operations = [
        migrations.RunPython(forwards_func),
    ]

Last edited 19 months ago by Michael (previous) (diff)
Note: See TracTickets for help on using tickets.
Back to Top