From 68e0a169c4f9fa7f8071e014b274fd59e970f9a3 Mon Sep 17 00:00:00 2001 From: Andrew Godwin Date: Tue, 30 Jul 2013 11:52:52 +0100 Subject: [PATCH] Rename pre_ and post_syncdb to *_migrate, with aliases from old names --- django/contrib/auth/management/__init__.py | 4 +- django/contrib/contenttypes/management.py | 2 +- .../gis/db/backends/spatialite/creation.py | 2 +- django/contrib/sites/management.py | 2 +- django/core/management/commands/flush.py | 22 ++--- django/core/management/commands/migrate.py | 21 +++-- django/core/management/sql.py | 16 ++-- django/db/backends/creation.py | 2 +- django/db/models/signals.py | 6 +- django/test/testcases.py | 11 ++- docs/ref/contrib/sites.txt | 2 +- docs/ref/signals.txt | 87 +++++++++++++------ docs/topics/testing/advanced.txt | 10 +-- 13 files changed, 113 insertions(+), 74 deletions(-) diff --git a/django/contrib/auth/management/__init__.py b/django/contrib/auth/management/__init__.py index 1f338469f8..e1b9be2e9b 100644 --- a/django/contrib/auth/management/__init__.py +++ b/django/contrib/auth/management/__init__.py @@ -187,7 +187,7 @@ def get_default_username(check_db=True): return '' return default_username -signals.post_syncdb.connect(create_permissions, +signals.post_migrate.connect(create_permissions, dispatch_uid="django.contrib.auth.management.create_permissions") -signals.post_syncdb.connect(create_superuser, +signals.post_migrate.connect(create_superuser, sender=auth_app, dispatch_uid="django.contrib.auth.management.create_superuser") diff --git a/django/contrib/contenttypes/management.py b/django/contrib/contenttypes/management.py index 64d1c418ef..21a34b2bfa 100644 --- a/django/contrib/contenttypes/management.py +++ b/django/contrib/contenttypes/management.py @@ -88,7 +88,7 @@ def update_all_contenttypes(verbosity=2, **kwargs): for app in get_apps(): update_contenttypes(app, None, verbosity, **kwargs) -signals.post_syncdb.connect(update_contenttypes) +signals.post_migrate.connect(update_contenttypes) if __name__ == "__main__": update_all_contenttypes() diff --git a/django/contrib/gis/db/backends/spatialite/creation.py b/django/contrib/gis/db/backends/spatialite/creation.py index d0a5f82033..2f0720ed84 100644 --- a/django/contrib/gis/db/backends/spatialite/creation.py +++ b/django/contrib/gis/db/backends/spatialite/creation.py @@ -45,7 +45,7 @@ class SpatiaLiteCreation(DatabaseCreation): # We need to then do a flush to ensure that any data installed by # custom SQL has been removed. The only test data should come from - # test fixtures, or autogenerated from post_syncdb triggers. + # test fixtures, or autogenerated from post_migrate triggers. # This has the side effect of loading initial data (which was # intentionally skipped in the syncdb). call_command('flush', diff --git a/django/contrib/sites/management.py b/django/contrib/sites/management.py index 7a29e82d4c..3ab49e5482 100644 --- a/django/contrib/sites/management.py +++ b/django/contrib/sites/management.py @@ -33,4 +33,4 @@ def create_default_site(app, created_models, verbosity, db, **kwargs): Site.objects.clear_cache() -signals.post_syncdb.connect(create_default_site, sender=site_app) +signals.post_migrate.connect(create_default_site, sender=site_app) diff --git a/django/core/management/commands/flush.py b/django/core/management/commands/flush.py index 95dd634d08..2ced3a2d60 100644 --- a/django/core/management/commands/flush.py +++ b/django/core/management/commands/flush.py @@ -6,7 +6,7 @@ from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS from django.core.management import call_command from django.core.management.base import NoArgsCommand, CommandError from django.core.management.color import no_style -from django.core.management.sql import sql_flush, emit_post_sync_signal +from django.core.management.sql import sql_flush, emit_post_migrate_signal from django.utils.importlib import import_module from django.utils.six.moves import input from django.utils import six @@ -23,8 +23,8 @@ class Command(NoArgsCommand): help='Tells Django not to load any initial data after database synchronization.'), ) help = ('Returns the database to the state it was in immediately after ' - 'syncdb was executed. This means that all data will be removed ' - 'from the database, any post-synchronization handlers will be ' + 'migrate was first executed. This means that all data will be removed ' + 'from the database, any post-migration handlers will be ' 're-executed, and the initial_data fixture will be re-installed.') def handle_noargs(self, **options): @@ -35,7 +35,7 @@ class Command(NoArgsCommand): # The following are stealth options used by Django's internals. reset_sequences = options.get('reset_sequences', True) allow_cascade = options.get('allow_cascade', False) - inhibit_post_syncdb = options.get('inhibit_post_syncdb', False) + inhibit_post_migrate = options.get('inhibit_post_migrate', False) self.style = no_style() @@ -54,7 +54,7 @@ class Command(NoArgsCommand): if interactive: confirm = input("""You have requested a flush of the database. This will IRREVERSIBLY DESTROY all data currently in the %r database, -and return each table to the state it was in after syncdb. +and return each table to a fresh state. Are you sure you want to do this? Type 'yes' to continue, or 'no' to cancel: """ % connection.settings_dict['NAME']) @@ -77,8 +77,8 @@ Are you sure you want to do this? "The full error: %s") % (connection.settings_dict['NAME'], e) six.reraise(CommandError, CommandError(new_msg), sys.exc_info()[2]) - if not inhibit_post_syncdb: - self.emit_post_syncdb(verbosity, interactive, db) + if not inhibit_post_migrate: + self.emit_post_migrate(verbosity, interactive, db) # Reinstall the initial_data fixture. if options.get('load_initial_data'): @@ -89,13 +89,13 @@ Are you sure you want to do this? self.stdout.write("Flush cancelled.\n") @staticmethod - def emit_post_syncdb(verbosity, interactive, database): - # Emit the post sync signal. This allows individual applications to - # respond as if the database had been sync'd from scratch. + def emit_post_migrate(verbosity, interactive, database): + # Emit the post migrate signal. This allows individual applications to + # respond as if the database had been migrated from scratch. all_models = [] for app in models.get_apps(): all_models.extend([ m for m in models.get_models(app, include_auto_created=True) if router.allow_syncdb(database, m) ]) - emit_post_sync_signal(set(all_models), verbosity, interactive, database) + emit_post_migrate_signal(set(all_models), verbosity, interactive, database) diff --git a/django/core/management/commands/migrate.py b/django/core/management/commands/migrate.py index d9a677e567..cf0e40e6c7 100644 --- a/django/core/management/commands/migrate.py +++ b/django/core/management/commands/migrate.py @@ -6,7 +6,7 @@ from django.conf import settings from django.core.management import call_command from django.core.management.base import BaseCommand, CommandError from django.core.management.color import color_style, no_style -from django.core.management.sql import custom_sql_for_model, emit_post_sync_signal, emit_pre_sync_signal +from django.core.management.sql import custom_sql_for_model, emit_post_migrate_signal, emit_pre_migrate_signal from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS from django.db.migrations.executor import MigrationExecutor from django.db.migrations.loader import AmbiguityError @@ -99,10 +99,14 @@ class Command(BaseCommand): # Run the syncdb phase. # If you ever manage to get rid of this, I owe you many, many drinks. + # Note that pre_migrate is called from inside here, as it needs + # the list of models about to be installed. if run_syncdb: if self.verbosity >= 1: self.stdout.write(self.style.MIGRATE_HEADING("Synchronizing apps without migrations:")) - self.sync_apps(connection, executor.loader.unmigrated_apps) + created_models = self.sync_apps(connection, executor.loader.unmigrated_apps) + else: + created_models = [] # Migrate! if self.verbosity >= 1: @@ -113,6 +117,10 @@ class Command(BaseCommand): else: executor.migrate(targets, plan, fake=options.get("fake", False)) + # Send the post_migrate signal, so individual apps can do whatever they need + # to do at this point. + emit_post_migrate_signal(created_models, self.verbosity, self.interactive, connection.alias) + def migration_progress_callback(self, action, migration): if self.verbosity >= 1: if action == "apply_start": @@ -159,7 +167,7 @@ class Command(BaseCommand): ) create_models = set([x for x in itertools.chain(*manifest.values())]) - emit_pre_sync_signal(create_models, self.verbosity, self.interactive, connection.alias) + emit_pre_migrate_signal(create_models, self.verbosity, self.interactive, connection.alias) # Create the tables for each model if self.verbosity >= 1: @@ -188,10 +196,6 @@ class Command(BaseCommand): # If you can prove we don't need this, remove it. transaction.set_dirty(using=connection.alias) - # Send the post_syncdb signal, so individual apps can do whatever they need - # to do at this point. - emit_post_sync_signal(created_models, self.verbosity, self.interactive, connection.alias) - # The connection may have been closed by a syncdb handler. cursor = connection.cursor() @@ -220,6 +224,7 @@ class Command(BaseCommand): if self.verbosity >= 1: self.stdout.write(" Installing indexes...\n") + # Install SQL indices for all newly created models for app_name, model_list in manifest.items(): for model in model_list: @@ -238,3 +243,5 @@ class Command(BaseCommand): # Load initial_data fixtures (unless that has been disabled) if self.load_initial_data: call_command('loaddata', 'initial_data', verbosity=self.verbosity, database=connection.alias, skip_validation=True) + + return created_models diff --git a/django/core/management/sql.py b/django/core/management/sql.py index b58d89f60a..4a61fcddb9 100644 --- a/django/core/management/sql.py +++ b/django/core/management/sql.py @@ -192,25 +192,25 @@ def custom_sql_for_model(model, style, connection): return output -def emit_pre_sync_signal(create_models, verbosity, interactive, db): - # Emit the pre_sync signal for every application. +def emit_pre_migrate_signal(create_models, verbosity, interactive, db): + # Emit the pre_migrate signal for every application. for app in models.get_apps(): app_name = app.__name__.split('.')[-2] if verbosity >= 2: - print("Running pre-sync handlers for application %s" % app_name) - models.signals.pre_syncdb.send(sender=app, app=app, + print("Running pre-migrate handlers for application %s" % app_name) + models.signals.pre_migrate.send(sender=app, app=app, create_models=create_models, verbosity=verbosity, interactive=interactive, db=db) -def emit_post_sync_signal(created_models, verbosity, interactive, db): - # Emit the post_sync signal for every application. +def emit_post_migrate_signal(created_models, verbosity, interactive, db): + # Emit the post_migrate signal for every application. for app in models.get_apps(): app_name = app.__name__.split('.')[-2] if verbosity >= 2: - print("Running post-sync handlers for application %s" % app_name) - models.signals.post_syncdb.send(sender=app, app=app, + print("Running post-migrate handlers for application %s" % app_name) + models.signals.post_migrate.send(sender=app, app=app, created_models=created_models, verbosity=verbosity, interactive=interactive, db=db) diff --git a/django/db/backends/creation.py b/django/db/backends/creation.py index 2ebbbbf2d5..51716a88bd 100644 --- a/django/db/backends/creation.py +++ b/django/db/backends/creation.py @@ -344,7 +344,7 @@ class BaseDatabaseCreation(object): # We need to then do a flush to ensure that any data installed by # custom SQL has been removed. The only test data should come from - # test fixtures, or autogenerated from post_syncdb triggers. + # test fixtures, or autogenerated from post_migrate triggers. # This has the side effect of loading initial data (which was # intentionally skipped in the syncdb). call_command('flush', diff --git a/django/db/models/signals.py b/django/db/models/signals.py index 3e321893c1..e53ffc3d1f 100644 --- a/django/db/models/signals.py +++ b/django/db/models/signals.py @@ -12,7 +12,9 @@ post_save = Signal(providing_args=["instance", "raw", "created", "using", "updat pre_delete = Signal(providing_args=["instance", "using"], use_caching=True) post_delete = Signal(providing_args=["instance", "using"], use_caching=True) -pre_syncdb = Signal(providing_args=["app", "create_models", "verbosity", "interactive", "db"]) -post_syncdb = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive", "db"], use_caching=True) +pre_migrate = Signal(providing_args=["app", "create_models", "verbosity", "interactive", "db"]) +pre_syncdb = pre_migrate +post_migrate = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive", "db"], use_caching=True) +post_syncdb = post_migrate m2m_changed = Signal(providing_args=["action", "instance", "reverse", "model", "pk_set", "using"], use_caching=True) diff --git a/django/test/testcases.py b/django/test/testcases.py index 6f3f1c00e4..5b72d4e8b8 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -718,7 +718,7 @@ class TransactionTestCase(SimpleTestCase): """Performs any pre-test setup. This includes: * If the class has an 'available_apps' attribute, restricting the app - cache to these applications, then firing post_syncdb -- it must run + cache to these applications, then firing post_migrate -- it must run with the correct set of applications for the test case. * If the class has a 'fixtures' attribute, installing these fixtures. """ @@ -726,8 +726,7 @@ class TransactionTestCase(SimpleTestCase): if self.available_apps is not None: cache.set_available_apps(self.available_apps) for db_name in self._databases_names(include_mirrors=False): - flush.Command.emit_post_syncdb( - verbosity=0, interactive=False, database=db_name) + flush.Command.emit_post_migrate(verbosity=0, interactive=False, database=db_name) try: self._fixture_setup() except Exception: @@ -772,7 +771,7 @@ class TransactionTestCase(SimpleTestCase): """Performs any post-test things. This includes: * Flushing the contents of the database, to leave a clean slate. If - the class has an 'available_apps' attribute, post_syncdb isn't fired. + the class has an 'available_apps' attribute, post_migrate isn't fired. * Force-closing the connection, so the next test gets a clean cursor. """ try: @@ -790,14 +789,14 @@ class TransactionTestCase(SimpleTestCase): cache.unset_available_apps() def _fixture_teardown(self): - # Allow TRUNCATE ... CASCADE and don't emit the post_syncdb signal + # Allow TRUNCATE ... CASCADE and don't emit the post_migrate signal # when flushing only a subset of the apps for db_name in self._databases_names(include_mirrors=False): call_command('flush', verbosity=0, interactive=False, database=db_name, skip_validation=True, reset_sequences=False, allow_cascade=self.available_apps is not None, - inhibit_post_syncdb=self.available_apps is not None) + inhibit_post_migrate=self.available_apps is not None) def assertQuerysetEqual(self, qs, values, transform=repr, ordered=True): items = six.moves.map(transform, qs) diff --git a/docs/ref/contrib/sites.txt b/docs/ref/contrib/sites.txt index 48f781310c..f6480cae3a 100644 --- a/docs/ref/contrib/sites.txt +++ b/docs/ref/contrib/sites.txt @@ -267,7 +267,7 @@ To enable the sites framework, follow these steps: 3. Run :djadmin:`migrate`. ``django.contrib.sites`` registers a -:data:`~django.db.models.signals.post_syncdb` signal handler which creates a +:data:`~django.db.models.signals.post_migrate` signal handler which creates a default site named ``example.com`` with the domain ``example.com``. This site will also be created after Django creates the test database. To set the correct name and domain for your project, you can use an :doc:`initial data diff --git a/docs/ref/signals.txt b/docs/ref/signals.txt index d4f261cadb..b988371aa7 100644 --- a/docs/ref/signals.txt +++ b/docs/ref/signals.txt @@ -360,40 +360,36 @@ Management signals Signals sent by :doc:`django-admin `. -pre_syncdb ----------- +pre_migrate +----------- -.. data:: django.db.models.signals.pre_syncdb +.. data:: django.db.models.signals.pre_migrate :module: -Sent by the :djadmin:`syncdb` command before it starts to install an +Sent by the :djadmin:`migrate` command before it starts to install an application. Any handlers that listen to this signal need to be written in a particular place: a ``management`` module in one of your :setting:`INSTALLED_APPS`. If handlers are registered anywhere else they may not be loaded by -:djadmin:`syncdb`. +:djadmin:`migrate`. Arguments sent with this signal: ``sender`` - The ``models`` module that was just installed. That is, if - :djadmin:`syncdb` just installed an app called ``"foo.bar.myapp"``, - ``sender`` will be the ``foo.bar.myapp.models`` module. + The ``models`` module of the app about to be migrated/synced. + For example, if :djadmin:`migrate` is about to install + an app called ``"foo.bar.myapp"``, ``sender`` will be the + ``foo.bar.myapp.models`` module. ``app`` Same as ``sender``. -``create_models`` - A list of the model classes from any app which :djadmin:`syncdb` plans to - create. - - ``verbosity`` Indicates how much information manage.py is printing on screen. See the :djadminopt:`--verbosity` flag for details. - Functions which listen for :data:`pre_syncdb` should adjust what they + Functions which listen for :data:`pre_migrate` should adjust what they output to the screen based on the value of this argument. ``interactive`` @@ -407,42 +403,57 @@ Arguments sent with this signal: ``db`` The alias of database on which a command will operate. -post_syncdb ------------ -.. data:: django.db.models.signals.post_syncdb +pre_syncdb +---------- + +.. data:: django.db.models.signals.pre_syncdb :module: -Sent by the :djadmin:`syncdb` command after it installs an application, and the +.. deprecated:: 1.7 + + This signal has been renamed to :data:`~django.db.models.signals.pre_migrate`. + +Alias of :data:`django.db.models.signals.pre_migrate`. As long as this alias +is present, for backwards-compatability this signal has an extra argument it sends: + +``create_models`` + A list of the model classes from any app which :djadmin:`migrate` is + going to create, **only if the app has no migrations**. + + +post_migrate +------------ + +.. data:: django.db.models.signals.post_migrate + :module: + +Sent by the :djadmin:`migrate` command after it installs an application, and the :djadmin:`flush` command. Any handlers that listen to this signal need to be written in a particular place: a ``management`` module in one of your :setting:`INSTALLED_APPS`. If handlers are registered anywhere else they may not be loaded by -:djadmin:`syncdb`. It is important that handlers of this signal perform +:djadmin:`migrate`. It is important that handlers of this signal perform idempotent changes (e.g. no database alterations) as this may cause the :djadmin:`flush` management command to fail if it also ran during the -:djadmin:`syncdb` command. +:djadmin:`migrate` command. Arguments sent with this signal: ``sender`` The ``models`` module that was just installed. That is, if - :djadmin:`syncdb` just installed an app called ``"foo.bar.myapp"``, + :djadmin:`migrate` just installed an app called ``"foo.bar.myapp"``, ``sender`` will be the ``foo.bar.myapp.models`` module. ``app`` Same as ``sender``. -``created_models`` - A list of the model classes from any app which :djadmin:`syncdb` has - created so far. - ``verbosity`` Indicates how much information manage.py is printing on screen. See the :djadminopt:`--verbosity` flag for details. - Functions which listen for :data:`post_syncdb` should adjust what they + Functions which listen for :data:`post_migrate` should adjust what they output to the screen based on the value of this argument. ``interactive`` @@ -459,14 +470,34 @@ Arguments sent with this signal: For example, ``yourapp/management/__init__.py`` could be written like:: - from django.db.models.signals import post_syncdb + from django.db.models.signals import post_migrate import yourapp.models def my_callback(sender, **kwargs): # Your specific logic here pass - post_syncdb.connect(my_callback, sender=yourapp.models) + post_migrate.connect(my_callback, sender=yourapp.models) + + +post_syncdb +----------- + +.. data:: django.db.models.signals.post_syncdb + :module: + +.. deprecated:: 1.7 + + This signal has been renamed to :data:`~django.db.models.signals.post_migrate`. + +Alias of :data:`django.db.models.signals.post_migrate`. As long as this alias +is present, for backwards-compatability this signal has an extra argument it sends: + +``created_models`` + A list of the model classes from any app which :djadmin:`migrate` has + created, **only if the app has no migrations**. + + Request/response signals ======================== diff --git a/docs/topics/testing/advanced.txt b/docs/topics/testing/advanced.txt index 4d7f22aaa2..6d9ea8d5c1 100644 --- a/docs/topics/testing/advanced.txt +++ b/docs/topics/testing/advanced.txt @@ -182,7 +182,7 @@ Advanced features of ``TransactionTestCase`` By default, ``available_apps`` is set to ``None``. After each test, Django calls :djadmin:`flush` to reset the database state. This empties all tables - and emits the :data:`~django.db.models.signals.post_syncdb` signal, which + and emits the :data:`~django.db.models.signals.post_migrate` signal, which re-creates one content type and three permissions for each model. This operation gets expensive proportionally to the number of models. @@ -190,13 +190,13 @@ Advanced features of ``TransactionTestCase`` behave as if only the models from these applications were available. The behavior of ``TransactionTestCase`` changes as follows: - - :data:`~django.db.models.signals.post_syncdb` is fired before each + - :data:`~django.db.models.signals.post_migrate` is fired before each test to create the content types and permissions for each model in available apps, in case they're missing. - After each test, Django empties only tables corresponding to models in available apps. However, at the database level, truncation may cascade to related models in unavailable apps. Furthermore - :data:`~django.db.models.signals.post_syncdb` isn't fired; it will be + :data:`~django.db.models.signals.post_migrate` isn't fired; it will be fired by the next ``TransactionTestCase``, after the correct set of applications is selected. @@ -205,10 +205,10 @@ Advanced features of ``TransactionTestCase`` cause unrelated tests to fail. Be careful with tests that use sessions; the default session engine stores them in the database. - Since :data:`~django.db.models.signals.post_syncdb` isn't emitted after + Since :data:`~django.db.models.signals.post_migrate` isn't emitted after flushing the database, its state after a ``TransactionTestCase`` isn't the same as after a ``TestCase``: it's missing the rows created by listeners - to :data:`~django.db.models.signals.post_syncdb`. Considering the + to :data:`~django.db.models.signals.post_migrate`. Considering the :ref:`order in which tests are executed `, this isn't an issue, provided either all ``TransactionTestCase`` in a given test suite declare ``available_apps``, or none of them.