Fixed #32395 -- Allowed capturing stdout of migration signals.
This commit is contained in:
parent
31bebc558b
commit
f23b05696e
|
@ -201,7 +201,7 @@ class Command(BaseCommand):
|
||||||
pre_migrate_state = executor._create_project_state(with_applied_migrations=True)
|
pre_migrate_state = executor._create_project_state(with_applied_migrations=True)
|
||||||
pre_migrate_apps = pre_migrate_state.apps
|
pre_migrate_apps = pre_migrate_state.apps
|
||||||
emit_pre_migrate_signal(
|
emit_pre_migrate_signal(
|
||||||
self.verbosity, self.interactive, connection.alias, apps=pre_migrate_apps, plan=plan,
|
self.verbosity, self.interactive, connection.alias, stdout=self.stdout, apps=pre_migrate_apps, plan=plan,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Run the syncdb phase.
|
# Run the syncdb phase.
|
||||||
|
@ -266,7 +266,7 @@ class Command(BaseCommand):
|
||||||
# Send the post_migrate signal, so individual apps can do whatever they need
|
# Send the post_migrate signal, so individual apps can do whatever they need
|
||||||
# to do at this point.
|
# to do at this point.
|
||||||
emit_post_migrate_signal(
|
emit_post_migrate_signal(
|
||||||
self.verbosity, self.interactive, connection.alias, apps=post_migrate_apps, plan=plan,
|
self.verbosity, self.interactive, connection.alias, stdout=self.stdout, apps=post_migrate_apps, plan=plan,
|
||||||
)
|
)
|
||||||
|
|
||||||
def migration_progress_callback(self, action, migration=None, fake=False):
|
def migration_progress_callback(self, action, migration=None, fake=False):
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
@ -21,7 +23,8 @@ def emit_pre_migrate_signal(verbosity, interactive, db, **kwargs):
|
||||||
if app_config.models_module is None:
|
if app_config.models_module is None:
|
||||||
continue
|
continue
|
||||||
if verbosity >= 2:
|
if verbosity >= 2:
|
||||||
print("Running pre-migrate handlers for application %s" % app_config.label)
|
stdout = kwargs.get('stdout', sys.stdout)
|
||||||
|
stdout.write('Running pre-migrate handlers for application %s' % app_config.label)
|
||||||
models.signals.pre_migrate.send(
|
models.signals.pre_migrate.send(
|
||||||
sender=app_config,
|
sender=app_config,
|
||||||
app_config=app_config,
|
app_config=app_config,
|
||||||
|
@ -38,7 +41,8 @@ def emit_post_migrate_signal(verbosity, interactive, db, **kwargs):
|
||||||
if app_config.models_module is None:
|
if app_config.models_module is None:
|
||||||
continue
|
continue
|
||||||
if verbosity >= 2:
|
if verbosity >= 2:
|
||||||
print("Running post-migrate handlers for application %s" % app_config.label)
|
stdout = kwargs.get('stdout', sys.stdout)
|
||||||
|
stdout.write('Running post-migrate handlers for application %s' % app_config.label)
|
||||||
models.signals.post_migrate.send(
|
models.signals.post_migrate.send(
|
||||||
sender=app_config,
|
sender=app_config,
|
||||||
app_config=app_config,
|
app_config=app_config,
|
||||||
|
|
|
@ -424,6 +424,11 @@ Arguments sent with this signal:
|
||||||
For example, the :mod:`django.contrib.auth` app only prompts to create a
|
For example, the :mod:`django.contrib.auth` app only prompts to create a
|
||||||
superuser when ``interactive`` is ``True``.
|
superuser when ``interactive`` is ``True``.
|
||||||
|
|
||||||
|
``stdout``
|
||||||
|
.. versionadded:: 4.0
|
||||||
|
|
||||||
|
A stream-like object where verbose output should be redirected.
|
||||||
|
|
||||||
``using``
|
``using``
|
||||||
The alias of database on which a command will operate.
|
The alias of database on which a command will operate.
|
||||||
|
|
||||||
|
@ -478,6 +483,11 @@ Arguments sent with this signal:
|
||||||
For example, the :mod:`django.contrib.auth` app only prompts to create a
|
For example, the :mod:`django.contrib.auth` app only prompts to create a
|
||||||
superuser when ``interactive`` is ``True``.
|
superuser when ``interactive`` is ``True``.
|
||||||
|
|
||||||
|
``stdout``
|
||||||
|
.. versionadded:: 4.0
|
||||||
|
|
||||||
|
A stream-like object where verbose output should be redirected.
|
||||||
|
|
||||||
``using``
|
``using``
|
||||||
The database alias used for synchronization. Defaults to the ``default``
|
The database alias used for synchronization. Defaults to the ``default``
|
||||||
database.
|
database.
|
||||||
|
|
|
@ -191,7 +191,11 @@ Serialization
|
||||||
Signals
|
Signals
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
* ...
|
* The new ``stdout`` argument for :func:`~django.db.models.signals.pre_migrate`
|
||||||
|
and :func:`~django.db.models.signals.post_migrate` signals allows redirecting
|
||||||
|
output to a stream-like object. It should be preferred over
|
||||||
|
:py:data:`sys.stdout` and :py:func:`print` when emitting verbose output in
|
||||||
|
order to allow proper capture when testing.
|
||||||
|
|
||||||
Templates
|
Templates
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.core import management
|
from django.core import management
|
||||||
from django.db import migrations
|
from django.db import migrations
|
||||||
|
@ -5,7 +7,7 @@ from django.db.models import signals
|
||||||
from django.test import TransactionTestCase, override_settings
|
from django.test import TransactionTestCase, override_settings
|
||||||
|
|
||||||
APP_CONFIG = apps.get_app_config('migrate_signals')
|
APP_CONFIG = apps.get_app_config('migrate_signals')
|
||||||
SIGNAL_ARGS = ['app_config', 'verbosity', 'interactive', 'using', 'plan', 'apps']
|
SIGNAL_ARGS = ['app_config', 'verbosity', 'interactive', 'using', 'stdout', 'plan', 'apps']
|
||||||
MIGRATE_DATABASE = 'default'
|
MIGRATE_DATABASE = 'default'
|
||||||
MIGRATE_VERBOSITY = 0
|
MIGRATE_VERBOSITY = 0
|
||||||
MIGRATE_INTERACTIVE = False
|
MIGRATE_INTERACTIVE = False
|
||||||
|
@ -69,7 +71,7 @@ class MigrateSignalTests(TransactionTestCase):
|
||||||
post_migrate_receiver = Receiver(signals.post_migrate)
|
post_migrate_receiver = Receiver(signals.post_migrate)
|
||||||
management.call_command(
|
management.call_command(
|
||||||
'migrate', database=MIGRATE_DATABASE, verbosity=MIGRATE_VERBOSITY,
|
'migrate', database=MIGRATE_DATABASE, verbosity=MIGRATE_VERBOSITY,
|
||||||
interactive=MIGRATE_INTERACTIVE,
|
interactive=MIGRATE_INTERACTIVE, stdout=StringIO('test_args'),
|
||||||
)
|
)
|
||||||
|
|
||||||
for receiver in [pre_migrate_receiver, post_migrate_receiver]:
|
for receiver in [pre_migrate_receiver, post_migrate_receiver]:
|
||||||
|
@ -81,6 +83,7 @@ class MigrateSignalTests(TransactionTestCase):
|
||||||
self.assertEqual(args['verbosity'], MIGRATE_VERBOSITY)
|
self.assertEqual(args['verbosity'], MIGRATE_VERBOSITY)
|
||||||
self.assertEqual(args['interactive'], MIGRATE_INTERACTIVE)
|
self.assertEqual(args['interactive'], MIGRATE_INTERACTIVE)
|
||||||
self.assertEqual(args['using'], 'default')
|
self.assertEqual(args['using'], 'default')
|
||||||
|
self.assertIn('test_args', args['stdout'].getvalue())
|
||||||
self.assertEqual(args['plan'], [])
|
self.assertEqual(args['plan'], [])
|
||||||
self.assertIsInstance(args['apps'], migrations.state.StateApps)
|
self.assertIsInstance(args['apps'], migrations.state.StateApps)
|
||||||
|
|
||||||
|
|
|
@ -39,10 +39,12 @@ class MigrateTests(MigrationTestBase):
|
||||||
self.assertTableNotExists("migrations_book")
|
self.assertTableNotExists("migrations_book")
|
||||||
# Run the migrations to 0001 only
|
# Run the migrations to 0001 only
|
||||||
stdout = io.StringIO()
|
stdout = io.StringIO()
|
||||||
call_command('migrate', 'migrations', '0001', verbosity=1, stdout=stdout, no_color=True)
|
call_command('migrate', 'migrations', '0001', verbosity=2, stdout=stdout, no_color=True)
|
||||||
stdout = stdout.getvalue()
|
stdout = stdout.getvalue()
|
||||||
self.assertIn('Target specific migration: 0001_initial, from migrations', stdout)
|
self.assertIn('Target specific migration: 0001_initial, from migrations', stdout)
|
||||||
self.assertIn('Applying migrations.0001_initial... OK', stdout)
|
self.assertIn('Applying migrations.0001_initial... OK', stdout)
|
||||||
|
self.assertIn('Running pre-migrate handlers for application migrations', stdout)
|
||||||
|
self.assertIn('Running post-migrate handlers for application migrations', stdout)
|
||||||
# The correct tables exist
|
# The correct tables exist
|
||||||
self.assertTableExists("migrations_author")
|
self.assertTableExists("migrations_author")
|
||||||
self.assertTableExists("migrations_tribble")
|
self.assertTableExists("migrations_tribble")
|
||||||
|
@ -55,10 +57,12 @@ class MigrateTests(MigrationTestBase):
|
||||||
self.assertTableExists("migrations_book")
|
self.assertTableExists("migrations_book")
|
||||||
# Unmigrate everything
|
# Unmigrate everything
|
||||||
stdout = io.StringIO()
|
stdout = io.StringIO()
|
||||||
call_command('migrate', 'migrations', 'zero', verbosity=1, stdout=stdout, no_color=True)
|
call_command('migrate', 'migrations', 'zero', verbosity=2, stdout=stdout, no_color=True)
|
||||||
stdout = stdout.getvalue()
|
stdout = stdout.getvalue()
|
||||||
self.assertIn('Unapply all migrations: migrations', stdout)
|
self.assertIn('Unapply all migrations: migrations', stdout)
|
||||||
self.assertIn('Unapplying migrations.0002_second... OK', stdout)
|
self.assertIn('Unapplying migrations.0002_second... OK', stdout)
|
||||||
|
self.assertIn('Running pre-migrate handlers for application migrations', stdout)
|
||||||
|
self.assertIn('Running post-migrate handlers for application migrations', stdout)
|
||||||
# Tables are gone
|
# Tables are gone
|
||||||
self.assertTableNotExists("migrations_author")
|
self.assertTableNotExists("migrations_author")
|
||||||
self.assertTableNotExists("migrations_tribble")
|
self.assertTableNotExists("migrations_tribble")
|
||||||
|
|
Loading…
Reference in New Issue