Fixed #30031 -- Added --no-header option to makemigrations/squashmigrations.

This commit is contained in:
Dakota Hawkins 2018-12-19 06:41:31 -05:00 committed by Carlton Gibson
parent b514dc14f4
commit 8d3147e130
6 changed files with 58 additions and 19 deletions

View File

@ -48,6 +48,10 @@ class Command(BaseCommand):
'-n', '--name', '-n', '--name',
help="Use this name for migration file(s).", help="Use this name for migration file(s).",
) )
parser.add_argument(
'--no-header', action='store_false', dest='include_header',
help='Do not add header comments to new migration file(s).',
)
parser.add_argument( parser.add_argument(
'--check', action='store_true', dest='check_changes', '--check', action='store_true', dest='check_changes',
help='Exit with a non-zero status if model changes are missing migrations.', help='Exit with a non-zero status if model changes are missing migrations.',
@ -63,6 +67,7 @@ class Command(BaseCommand):
self.migration_name = options['name'] self.migration_name = options['name']
if self.migration_name and not self.migration_name.isidentifier(): if self.migration_name and not self.migration_name.isidentifier():
raise CommandError('The migration name must be a valid Python identifier.') raise CommandError('The migration name must be a valid Python identifier.')
self.include_header = options['include_header']
check_changes = options['check_changes'] check_changes = options['check_changes']
# Make sure the app they asked for exists # Make sure the app they asked for exists
@ -188,7 +193,7 @@ class Command(BaseCommand):
self.stdout.write(self.style.MIGRATE_HEADING("Migrations for '%s':" % app_label) + "\n") self.stdout.write(self.style.MIGRATE_HEADING("Migrations for '%s':" % app_label) + "\n")
for migration in app_migrations: for migration in app_migrations:
# Describe the migration # Describe the migration
writer = MigrationWriter(migration) writer = MigrationWriter(migration, self.include_header)
if self.verbosity >= 1: if self.verbosity >= 1:
# Display a relative path if it's below the current working # Display a relative path if it's below the current working
# directory, or an absolute path otherwise. # directory, or an absolute path otherwise.
@ -288,7 +293,7 @@ class Command(BaseCommand):
self.migration_name or ("merge_%s" % get_migration_name_timestamp()) self.migration_name or ("merge_%s" % get_migration_name_timestamp())
) )
new_migration = subclass(migration_name, app_label) new_migration = subclass(migration_name, app_label)
writer = MigrationWriter(new_migration) writer = MigrationWriter(new_migration, self.include_header)
if not self.dry_run: if not self.dry_run:
# Write the merge migrations file to the disk # Write the merge migrations file to the disk

View File

@ -37,6 +37,10 @@ class Command(BaseCommand):
'--squashed-name', '--squashed-name',
help='Sets the name of the new squashed migration.', help='Sets the name of the new squashed migration.',
) )
parser.add_argument(
'--no-header', action='store_false', dest='include_header',
help='Do not add a header comment to the new squashed migration.',
)
def handle(self, **options): def handle(self, **options):
@ -47,6 +51,7 @@ class Command(BaseCommand):
migration_name = options['migration_name'] migration_name = options['migration_name']
no_optimize = options['no_optimize'] no_optimize = options['no_optimize']
squashed_name = options['squashed_name'] squashed_name = options['squashed_name']
include_header = options['include_header']
# Validate app_label. # Validate app_label.
try: try:
apps.get_app_config(app_label) apps.get_app_config(app_label)
@ -178,7 +183,7 @@ class Command(BaseCommand):
new_migration.initial = True new_migration.initial = True
# Write out the new migration file # Write out the new migration file
writer = MigrationWriter(new_migration) writer = MigrationWriter(new_migration, include_header)
with open(writer.path, "w", encoding='utf-8') as fh: with open(writer.path, "w", encoding='utf-8') as fh:
fh.write(writer.as_string()) fh.write(writer.as_string())

View File

@ -132,8 +132,9 @@ class MigrationWriter:
of the migration file from it. of the migration file from it.
""" """
def __init__(self, migration): def __init__(self, migration, include_header=True):
self.migration = migration self.migration = migration
self.include_header = include_header
self.needs_manual_porting = False self.needs_manual_porting = False
def as_string(self): def as_string(self):
@ -195,10 +196,13 @@ class MigrationWriter:
if self.migration.replaces: if self.migration.replaces:
items['replaces_str'] = "\n replaces = %s\n" % self.serialize(self.migration.replaces)[0] items['replaces_str'] = "\n replaces = %s\n" % self.serialize(self.migration.replaces)[0]
# Hinting that goes into comment # Hinting that goes into comment
items.update( if self.include_header:
version=get_version(), items['migration_header'] = MIGRATION_HEADER_TEMPLATE % {
timestamp=now().strftime("%Y-%m-%d %H:%M"), 'version': get_version(),
) 'timestamp': now().strftime("%Y-%m-%d %H:%M"),
}
else:
items['migration_header'] = ""
if self.migration.initial: if self.migration.initial:
items['initial_str'] = "\n initial = True\n" items['initial_str'] = "\n initial = True\n"
@ -279,10 +283,14 @@ class MigrationWriter:
return serializer_factory(value).serialize() return serializer_factory(value).serialize()
MIGRATION_TEMPLATE = """\ MIGRATION_HEADER_TEMPLATE = """\
# Generated by Django %(version)s on %(timestamp)s # Generated by Django %(version)s on %(timestamp)s
%(imports)s """
MIGRATION_TEMPLATE = """\
%(migration_header)s%(imports)s
class Migration(migrations.Migration): class Migration(migrations.Migration):
%(replaces_str)s%(initial_str)s %(replaces_str)s%(initial_str)s

View File

@ -776,6 +776,12 @@ Enables fixing of migration conflicts.
Allows naming the generated migration(s) instead of using a generated name. The Allows naming the generated migration(s) instead of using a generated name. The
name must be a valid Python :ref:`identifier <python:identifiers>`. name must be a valid Python :ref:`identifier <python:identifiers>`.
.. django-admin-option:: --no-header
.. versionadded:: 2.2
Generate migration files without Django version and timestamp header.
.. django-admin-option:: --check .. django-admin-option:: --check
Makes ``makemigrations`` exit with a non-zero status when model changes without Makes ``makemigrations`` exit with a non-zero status when model changes without
@ -1156,6 +1162,12 @@ Suppresses all user prompts.
Sets the name of the squashed migration. When omitted, the name is based on the Sets the name of the squashed migration. When omitted, the name is based on the
first and last migration, with ``_squashed_`` in between. first and last migration, with ``_squashed_`` in between.
.. django-admin-option:: --no-header
.. versionadded:: 2.2
Generate squashed migration file without Django version and timestamp header.
``startapp`` ``startapp``
------------ ------------

View File

@ -194,6 +194,10 @@ Management Commands
* On Oracle, :djadmin:`dbshell` is wrapped with ``rlwrap``, if available. * On Oracle, :djadmin:`dbshell` is wrapped with ``rlwrap``, if available.
``rlwrap`` provides a command history and editing of keyboard input. ``rlwrap`` provides a command history and editing of keyboard input.
* The new :option:`makemigrations --no-header` option avoids writing header
comments in generated migration file(s). This option is also available for
:djadmin:`squashmigrations`.
Migrations Migrations
~~~~~~~~~~ ~~~~~~~~~~

View File

@ -613,16 +613,21 @@ class WriterTests(SimpleTestCase):
}) })
dt = datetime.datetime(2015, 7, 31, 4, 40, 0, 0, tzinfo=utc) dt = datetime.datetime(2015, 7, 31, 4, 40, 0, 0, tzinfo=utc)
with mock.patch('django.db.migrations.writer.now', lambda: dt): with mock.patch('django.db.migrations.writer.now', lambda: dt):
writer = MigrationWriter(migration) for include_header in (True, False):
output = writer.as_string() with self.subTest(include_header=include_header):
writer = MigrationWriter(migration, include_header)
output = writer.as_string()
self.assertTrue( self.assertEqual(
output.startswith( include_header,
"# Generated by Django %(version)s on 2015-07-31 04:40\n" % { output.startswith(
'version': get_version(), "# Generated by Django %s on 2015-07-31 04:40\n\n" % get_version()
} )
) )
) if not include_header:
# Make sure the output starts with something that's not
# a comment or indentation or blank line
self.assertRegex(output.splitlines(keepends=True)[0], r"^[^#\s]+")
def test_models_import_omitted(self): def test_models_import_omitted(self):
""" """