Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed #29198 -- Added --plan option to migrate command. #9968

Merged
merged 1 commit into from Aug 3, 2018

Conversation

cgdeboer
Copy link

@cgdeboer cgdeboer commented May 19, 2018

Summary

Initial Stab at the ticket #29198 (https://code.djangoproject.com/ticket/29198)

Highlights

  • Show the planned migrations in the order they will be applied with each of the operations in the order they will be applied.
  • Ensure that we respect the direction of the migration when describing the operation (i.e backwards and forwards)
  • If an operation in the reverse migration plan is irreversible, say so (with warning text style). See Fig 3
  • Try to describe SQL and Python operations with a little bit of detail.

Fig 1: Forwards

image

Fig 2: Backwards

image

Fig 3: Backwards with Warning

image

@cgdeboer cgdeboer changed the title WIP: Fixed #29198 Fixed #29198 May 21, 2018
@cgdeboer cgdeboer changed the title Fixed #29198 Fixed #29198 -- Add a "--plan" option to the migrate command May 22, 2018
@carltongibson carltongibson changed the title Fixed #29198 -- Add a "--plan" option to the migrate command Fixed #29198 -- Added --plan option to migrate command. Jul 11, 2018
Copy link
Member

@carltongibson carltongibson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @cgdeboer. Thanks for this. Looking good.

I've rebased, squashed the commits and adjusted the commit message to follow the guidelines.

I just had a couple of comments at this stage...

action = operation.sql if backwards is not True else operation.reverse_sql
prefix = ""
else:
action = "No further Detail"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about the utility of --> No further Detail. In this case it might be better to just leave it off.

(Maybe it's useful. Thoughts?)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially, I was thinking about each action saying at least something to preserve the cadence of the output, but I agree it probably is completely superfluous. I'll remove it.

.. django-admin-option:: --plan

Shows the order of unapplied migration operations Django will apply for the
given ``migrate`` command. Unlike the similar ``showmigrations --plan``

This comment was marked as resolved.

This comment was marked as resolved.

@cgdeboer
Copy link
Author

@carltongibson thanks for squashing, I added a couple commits to address your comments.

self.assertEqual(
"Planned operations:\n"
"migrations.0003_third\n"
" Undo Create model Author --> \n"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's slightly more complex but I had imagined we'd not have the --> if there was nothing after it.

What do you think?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point, I'll get that cleaned up.

Copy link
Member

@carltongibson carltongibson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @cgdeboer.

I like this. I think it should be useful.

Are you able to squash the commits again? (It seems like you re-merged master, or the single commit, back into your feature branch: you needed to reset to the branch here that I pushed to.

If your fork is origin locally:

git fetch origin
git reset --hard origin/ticket_29198

But now we need to squash and git push -f ... again. Are you able to do that?
)

" Raw SQL operation --> SELECT * FROM migrations_salamander\n",
out.getvalue()
)
call_command("migrate", "migrations", "zero", verbosity=0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other test cases have a comment here:

# Cleanup by unmigrating everything

I think that's probably worthwhile (since I did a double-take myself reviewing even now).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add that , re-squash and get the commit message in line. Long time user, first time contributor (to django), so pardon the missteps, and thanks for your patience.

@cgdeboer cgdeboer force-pushed the ticket_29198 branch 2 times, most recently from b874e05 to 1bfb989 Compare July 13, 2018 00:00
@cgdeboer
Copy link
Author

@carltongibson got those comments resolved, commits squashed and cleaned up, etc. Tests are passing locally, not sure if I've done something to offend Jenkins, but he doesn't look completely satisfied.

@cgdeboer
Copy link
Author

@carltongibson let me know if I need that squashin' right.

Copy link
Member

@carltongibson carltongibson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @cgdeboer.

This looks great.

I just left a couple of small comments. Can you address those and rebase, squash and force push again? (Hopefully the CI run should pass this time: failures are unrelated.)

From there we should be able to get this in. Thanks for the contribution!

``migrate --plan`` command shows only the migrations, and operations, to be
performed.

This is intended for users to have an extra sanity check before performing
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite sure about the phrasing here. Maybe:

This is intended to allow users to verify operations before performing a more
advanced set of migrations.

Tests --plan output of migrate command
"""
out = io.StringIO()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the blank line.

call_command("migrate", "migrations", verbosity=0)

out = io.StringIO()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the blank line

@@ -14,6 +14,31 @@ def is_referenced_by_foreign_key(state, model_name_lower, field, field_name):
return False


def verbose_describe(operation, backwards):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% convinced on the function name here. Maybe someone has a suggestion?
(Maybe it's fine.)

Copy link
Member

@carltongibson carltongibson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. This looks great. Thanks @cgdeboer.

(The CI failure is unrelated.)

@@ -54,6 +55,10 @@ def add_arguments(self, parser):
'--run-syncdb', action='store_true',
help='Creates tables for apps without migrations.',
)
parser.add_argument(
'--plan', action='store_true', dest='plan',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dest='plan', is unneeded as that's the default value from --plan (eac9ab7)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix


if options['plan']:
self.stdout.write("Planned operations:", self.style.MIGRATE_LABEL)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use single quotes throughout the patch except if the string contains a single quote.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I certainly can do that, though I was trying to emulate the pattern seen below in the file in master.

Lines 156, 187, 192, 195, 309, etc, all use double quotes, even when there are no single quotes.

Please confirm you'd like to shift from that pattern.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, for new code we're trying to follow the style guide.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect.

@@ -14,6 +14,31 @@ def is_referenced_by_foreign_key(state, model_name_lower, field, field_name):
return False


def describe_operation(operation, backwards):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking this would be better organized as a method of the migrate command.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I'll move it there.

@@ -14,6 +14,31 @@ def is_referenced_by_foreign_key(state, model_name_lower, field, field_name):
return False


def describe_operation(operation, backwards):
"""
Returns a string, intended for `stdout.write`, that describes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

verb style: Return

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix

action = ""
prefix = "Undo " if backwards is True else ""
if action is None:
error = True
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This branch is untested.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True... will fix.

Shows the order of unapplied migration operations Django will apply for the
given ``migrate`` command. Unlike the similar ``showmigrations --plan``
command which lists all migrations (even applied ones), the
``migrate --plan`` command shows only the migrations, and operations, to be
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drop commas around "and operations"

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix

``migrate --plan`` command shows only the migrations, and operations, to be
performed.

This is intended to allow users to verify operations before performing a more
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this sentence adds value.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. I'll rm it.

else:
action = "{}".format(action.replace("\n", ""))
error = False
action = action if len(action) < 40 else '{}...'.format(action[:37])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a test for the truncation logic and make sure that all branches in this function are exercised by tests. This could use django.utils.text.Truncator

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take a look.

@@ -54,6 +55,10 @@ def add_arguments(self, parser):
'--run-syncdb', action='store_true',
help='Creates tables for apps without migrations.',
)
parser.add_argument(
'--plan', action='store_true', dest='plan',
help='Shows a list of ordered migration actions that Django will perform.',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ordered -> the ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just to confirm here. this comment is about the use of the "zero article" vs the "definite article" ? We want:

Shows a list of the ordered migration actions that Django will perform.

Correct?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think "ordered" is also unnecessary (i.e. why would they be out of order ;-) ).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I thought 'ordered’ would be a good cue. Since the semi-related showmigrations command does not list migrations in order by default.

I can ditch ordered and we will presume the reader knows that any actions will be ordered

action = "{}".format(action.replace("\n", ""))
error = False
action = action if len(action) < 40 else '{}...'.format(action[:37])
styled_action = " -> {}".format(action) if action else action
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this would be simpler:

if action:
    action = '  -> ` + string

I don't think the variable name needs to change.

@cgdeboer cgdeboer force-pushed the ticket_29198 branch 2 times, most recently from a3d23be to 035ce94 Compare July 29, 2018 02:57
@cgdeboer
Copy link
Author

@timgraham I think I got all the comments addressed.

Copy link
Member

@timgraham timgraham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking better. I force pushed a few cosmetic updates.

How about displaying a message instead just "Planned operations:" if there isn't anything to do?

@@ -268,6 +268,68 @@ def test_showmigrations_plan(self):
# Cleanup by unmigrating everything
call_command("migrate", "migrations", "zero", verbosity=0)

@override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_plan"})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use also use single quotes in the test where possible.

a migration operation.
"""
if hasattr(operation, 'code'):
action = operation.code.__doc__ if not backwards else operation.reverse_code.__doc__
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like operation.reverse_code.__doc__ will fail with AttributeError if operation.reverse_code is None.

@timgraham timgraham force-pushed the ticket_29198 branch 2 times, most recently from 2d8ee8b to 7c9f632 Compare July 30, 2018 14:39
@cgdeboer
Copy link
Author

How about displaying a message instead just "Planned operations:" if there isn't anything to do?

Following the pattern of other areas of the migrate management command. It seems a pattern like:

Planned operations:
  No planned migration operations.

follows the pattern of:

Running migrations:
  No migrations to apply.

updated and re-pushed

@cgdeboer cgdeboer force-pushed the ticket_29198 branch 2 times, most recently from cdc285b to de65aec Compare August 1, 2018 11:03
Copy link
Member

@timgraham timgraham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two fixes look good, but please add tests for them also.

"""Tests migrate --plan output."""
out = io.StringIO()
# Show the plan up to the third migration.
call_command("migrate", "migrations", "0003", plan=True, stdout=out, no_color=True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use single quotes throughout the test.

@cgdeboer
Copy link
Author

cgdeboer commented Aug 2, 2018

Done and pushed.

@timgraham timgraham merged commit 058d33f into django:master Aug 3, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants