Refactored the migration signals to use app configs.

De-aliased pre/post_syncdb to pre/post_migrate to increase
backwards-compatibility.
This commit is contained in:
Aymeric Augustin 2013-12-27 20:55:58 +01:00
parent 5782c94f23
commit 00110904ac
11 changed files with 187 additions and 76 deletions

View File

@ -60,18 +60,21 @@ def _check_permission_clashing(custom, builtin, ctype):
pool.add(codename) pool.add(codename)
def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kwargs): def create_permissions(app_config, verbosity=22, interactive=True, db=DEFAULT_DB_ALIAS, **kwargs):
if not app_config.models_module:
return
try: try:
apps.get_model('auth', 'Permission') Permission = apps.get_model('auth', 'Permission')
except LookupError: except LookupError:
return return
if not router.allow_migrate(db, auth_app.Permission): if not router.allow_migrate(db, Permission):
return return
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
app_models = apps.get_models(app) app_models = apps.get_models(app_config.models_module)
# This will hold the permissions we're looking for as # This will hold the permissions we're looking for as
# (content_type, (codename, name)) # (content_type, (codename, name))
@ -89,20 +92,20 @@ def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kw
# Find all the Permissions that have a content_type for a model we're # Find all the Permissions that have a content_type for a model we're
# looking for. We don't need to check for codenames since we already have # looking for. We don't need to check for codenames since we already have
# a list of the ones we're going to create. # a list of the ones we're going to create.
all_perms = set(auth_app.Permission.objects.using(db).filter( all_perms = set(Permission.objects.using(db).filter(
content_type__in=ctypes, content_type__in=ctypes,
).values_list( ).values_list(
"content_type", "codename" "content_type", "codename"
)) ))
perms = [ perms = [
auth_app.Permission(codename=codename, name=name, content_type=ctype) Permission(codename=codename, name=name, content_type=ctype)
for ctype, (codename, name) in searched_perms for ctype, (codename, name) in searched_perms
if (ctype.pk, codename) not in all_perms if (ctype.pk, codename) not in all_perms
] ]
# Validate the permissions before bulk_creation to avoid cryptic # Validate the permissions before bulk_creation to avoid cryptic
# database error when the verbose_name is longer than 50 characters # database error when the verbose_name is longer than 50 characters
permission_name_max_length = auth_app.Permission._meta.get_field('name').max_length permission_name_max_length = Permission._meta.get_field('name').max_length
verbose_name_max_length = permission_name_max_length - 11 # len('Can change ') prefix verbose_name_max_length = permission_name_max_length - 11 # len('Can change ') prefix
for perm in perms: for perm in perms:
if len(perm.name) > permission_name_max_length: if len(perm.name) > permission_name_max_length:
@ -112,13 +115,13 @@ def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kw
verbose_name_max_length, verbose_name_max_length,
) )
) )
auth_app.Permission.objects.using(db).bulk_create(perms) Permission.objects.using(db).bulk_create(perms)
if verbosity >= 2: if verbosity >= 2:
for perm in perms: for perm in perms:
print("Adding permission '%s'" % perm) print("Adding permission '%s'" % perm)
def create_superuser(app, created_models, verbosity, db, **kwargs): def create_superuser(app_config, verbosity=22, interactive=True, db=DEFAULT_DB_ALIAS, **kwargs):
try: try:
apps.get_model('auth', 'Permission') apps.get_model('auth', 'Permission')
except LookupError: except LookupError:
@ -128,7 +131,7 @@ def create_superuser(app, created_models, verbosity, db, **kwargs):
from django.core.management import call_command from django.core.management import call_command
if UserModel in created_models and kwargs.get('interactive', True): if not UserModel.objects.exists() and interactive:
msg = ("\nYou just installed Django's auth system, which means you " msg = ("\nYou just installed Django's auth system, which means you "
"don't have any superusers defined.\nWould you like to create one " "don't have any superusers defined.\nWould you like to create one "
"now? (yes/no): ") "now? (yes/no): ")
@ -203,7 +206,9 @@ def get_default_username(check_db=True):
return '' return ''
return default_username return default_username
signals.post_migrate.connect(create_permissions, signals.post_migrate.connect(create_permissions,
dispatch_uid="django.contrib.auth.management.create_permissions") dispatch_uid="django.contrib.auth.management.create_permissions")
signals.post_migrate.connect(create_superuser, signals.post_migrate.connect(create_superuser,
sender=auth_app, dispatch_uid="django.contrib.auth.management.create_superuser") sender=apps.get_app_config('auth'),
dispatch_uid="django.contrib.auth.management.create_superuser")

View File

@ -232,13 +232,15 @@ class PermissionTestCase(TestCase):
Test that we show proper error message if we are trying to create Test that we show proper error message if we are trying to create
duplicate permissions. duplicate permissions.
""" """
auth_app_config = apps.get_app_config('auth')
# check duplicated default permission # check duplicated default permission
models.Permission._meta.permissions = [ models.Permission._meta.permissions = [
('change_permission', 'Can edit permission (duplicate)')] ('change_permission', 'Can edit permission (duplicate)')]
six.assertRaisesRegex(self, CommandError, six.assertRaisesRegex(self, CommandError,
"The permission codename 'change_permission' clashes with a " "The permission codename 'change_permission' clashes with a "
"builtin permission for model 'auth.Permission'.", "builtin permission for model 'auth.Permission'.",
create_permissions, models, [], verbosity=0) create_permissions, auth_app_config, verbosity=0)
# check duplicated custom permissions # check duplicated custom permissions
models.Permission._meta.permissions = [ models.Permission._meta.permissions = [
@ -249,21 +251,23 @@ class PermissionTestCase(TestCase):
six.assertRaisesRegex(self, CommandError, six.assertRaisesRegex(self, CommandError,
"The permission codename 'my_custom_permission' is duplicated for model " "The permission codename 'my_custom_permission' is duplicated for model "
"'auth.Permission'.", "'auth.Permission'.",
create_permissions, models, [], verbosity=0) create_permissions, auth_app_config, verbosity=0)
# should not raise anything # should not raise anything
models.Permission._meta.permissions = [ models.Permission._meta.permissions = [
('my_custom_permission', 'Some permission'), ('my_custom_permission', 'Some permission'),
('other_one', 'Some other permission'), ('other_one', 'Some other permission'),
] ]
create_permissions(models, [], verbosity=0) create_permissions(auth_app_config, verbosity=0)
def test_default_permissions(self): def test_default_permissions(self):
auth_app_config = apps.get_app_config('auth')
permission_content_type = ContentType.objects.get_by_natural_key('auth', 'permission') permission_content_type = ContentType.objects.get_by_natural_key('auth', 'permission')
models.Permission._meta.permissions = [ models.Permission._meta.permissions = [
('my_custom_permission', 'Some permission'), ('my_custom_permission', 'Some permission'),
] ]
create_permissions(models, [], verbosity=0) create_permissions(auth_app_config, verbosity=0)
# add/change/delete permission by default + custom permission # add/change/delete permission by default + custom permission
self.assertEqual(models.Permission.objects.filter( self.assertEqual(models.Permission.objects.filter(
@ -272,7 +276,7 @@ class PermissionTestCase(TestCase):
models.Permission.objects.filter(content_type=permission_content_type).delete() models.Permission.objects.filter(content_type=permission_content_type).delete()
models.Permission._meta.default_permissions = [] models.Permission._meta.default_permissions = []
create_permissions(models, [], verbosity=0) create_permissions(auth_app_config, verbosity=0)
# custom permission only since default permissions is empty # custom permission only since default permissions is empty
self.assertEqual(models.Permission.objects.filter( self.assertEqual(models.Permission.objects.filter(
@ -280,10 +284,12 @@ class PermissionTestCase(TestCase):
).count(), 1) ).count(), 1)
def test_verbose_name_length(self): def test_verbose_name_length(self):
auth_app_config = apps.get_app_config('auth')
permission_content_type = ContentType.objects.get_by_natural_key('auth', 'permission') permission_content_type = ContentType.objects.get_by_natural_key('auth', 'permission')
models.Permission.objects.filter(content_type=permission_content_type).delete() models.Permission.objects.filter(content_type=permission_content_type).delete()
models.Permission._meta.verbose_name = "some ridiculously long verbose name that is out of control" models.Permission._meta.verbose_name = "some ridiculously long verbose name that is out of control"
six.assertRaisesRegex(self, exceptions.ValidationError, six.assertRaisesRegex(self, exceptions.ValidationError,
"The verbose_name of permission is longer than 39 characters", "The verbose_name of permission is longer than 39 characters",
create_permissions, models, [], verbosity=0) create_permissions, auth_app_config, verbosity=0)

View File

@ -1,5 +1,4 @@
from django.apps import apps from django.apps import apps
from django.contrib.contenttypes.models import ContentType
from django.db import DEFAULT_DB_ALIAS, router from django.db import DEFAULT_DB_ALIAS, router
from django.db.models import signals from django.db.models import signals
from django.utils.encoding import smart_text from django.utils.encoding import smart_text
@ -7,13 +6,16 @@ from django.utils import six
from django.utils.six.moves import input from django.utils.six.moves import input
def update_contenttypes(app, created_models, verbosity=2, db=DEFAULT_DB_ALIAS, **kwargs): def update_contenttypes(app_config, verbosity=2, interactive=True, db=DEFAULT_DB_ALIAS, **kwargs):
""" """
Creates content types for models in the given app, removing any model Creates content types for models in the given app, removing any model
entries that no longer have a matching model class. entries that no longer have a matching model class.
""" """
if not app_config.models_module:
return
try: try:
apps.get_model('contenttypes', 'ContentType') ContentType = apps.get_model('contenttypes', 'ContentType')
except LookupError: except LookupError:
return return
@ -21,7 +23,7 @@ def update_contenttypes(app, created_models, verbosity=2, db=DEFAULT_DB_ALIAS, *
return return
ContentType.objects.clear_cache() ContentType.objects.clear_cache()
app_models = apps.get_models(app) app_models = apps.get_models(app_config.models_module)
if not app_models: if not app_models:
return return
# They all have the same app_label, get the first one. # They all have the same app_label, get the first one.
@ -85,11 +87,13 @@ If you're unsure, answer 'no'.
print("Stale content types remain.") print("Stale content types remain.")
def update_all_contenttypes(verbosity=2, **kwargs): def update_all_contenttypes(**kwargs):
for app_config in apps.get_app_configs(only_with_models_module=True): for app_config in apps.get_app_configs(only_with_models_module=True):
update_contenttypes(app_config.models_module, None, verbosity, **kwargs) update_contenttypes(app_config, **kwargs)
signals.post_migrate.connect(update_contenttypes) signals.post_migrate.connect(update_contenttypes)
if __name__ == "__main__": if __name__ == "__main__":
update_all_contenttypes() update_all_contenttypes()

View File

@ -2,17 +2,22 @@
Creates the default Site object. Creates the default Site object.
""" """
from django.db.models import signals from django.apps import apps
from django.db import connections
from django.db import router
from django.contrib.sites.models import Site
from django.contrib.sites import models as site_app
from django.core.management.color import no_style from django.core.management.color import no_style
from django.db import DEFAULT_DB_ALIAS, connections, router
from django.db.models import signals
def create_default_site(app, created_models, verbosity, db, **kwargs): def create_default_site(app_config, verbosity=22, interactive=True, db=DEFAULT_DB_ALIAS, **kwargs):
# Only create the default sites in databases where Django created the table try:
if Site in created_models and router.allow_migrate(db, Site): Site = apps.get_model('sites', 'Site')
except LookupError:
return
if not router.allow_migrate(db, Site):
return
if not Site.objects.exists():
# The default settings set SITE_ID = 1, and some tests in Django's test # The default settings set SITE_ID = 1, and some tests in Django's test
# suite rely on this value. However, if database sequences are reused # suite rely on this value. However, if database sequences are reused
# (e.g. in the test suite after flush/syncdb), it isn't guaranteed that # (e.g. in the test suite after flush/syncdb), it isn't guaranteed that
@ -34,4 +39,5 @@ def create_default_site(app, created_models, verbosity, db, **kwargs):
Site.objects.clear_cache() Site.objects.clear_cache()
signals.post_migrate.connect(create_default_site, sender=site_app)
signals.post_migrate.connect(create_default_site, sender=apps.get_app_config('sites'))

View File

@ -211,6 +211,13 @@ def emit_pre_migrate_signal(create_models, verbosity, interactive, db):
if verbosity >= 2: if verbosity >= 2:
print("Running pre-migrate handlers for application %s" % app_config.label) print("Running pre-migrate handlers for application %s" % app_config.label)
models.signals.pre_migrate.send( models.signals.pre_migrate.send(
sender=app_config,
app_config=app_config,
verbosity=verbosity,
interactive=interactive,
db=db)
# For backwards-compatibility -- remove in Django 1.9.
models.signals.pre_syncdb.send(
sender=app_config.models_module, sender=app_config.models_module,
app=app_config.models_module, app=app_config.models_module,
create_models=create_models, create_models=create_models,
@ -225,6 +232,13 @@ def emit_post_migrate_signal(created_models, verbosity, interactive, db):
if verbosity >= 2: if verbosity >= 2:
print("Running post-migrate handlers for application %s" % app_config.label) print("Running post-migrate handlers for application %s" % app_config.label)
models.signals.post_migrate.send( models.signals.post_migrate.send(
sender=app_config,
app_config=app_config,
verbosity=verbosity,
interactive=interactive,
db=db)
# For backwards-compatibility -- remove in Django 1.9.
models.signals.post_syncdb.send(
sender=app_config.models_module, sender=app_config.models_module,
app=app_config.models_module, app=app_config.models_module,
created_models=created_models, created_models=created_models,

View File

@ -62,7 +62,8 @@ post_delete = ModelSignal(providing_args=["instance", "using"], use_caching=True
m2m_changed = ModelSignal(providing_args=["action", "instance", "reverse", "model", "pk_set", "using"], use_caching=True) m2m_changed = ModelSignal(providing_args=["action", "instance", "reverse", "model", "pk_set", "using"], use_caching=True)
pre_migrate = Signal(providing_args=["app", "create_models", "verbosity", "interactive", "db"]) pre_migrate = Signal(providing_args=["app_config", "verbosity", "interactive", "db"])
pre_syncdb = pre_migrate post_migrate = Signal(providing_args=["app_config", "verbosity", "interactive", "db"])
post_migrate = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive", "db"])
post_syncdb = post_migrate pre_syncdb = Signal(providing_args=["app", "create_models", "verbosity", "interactive", "db"])
post_syncdb = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive", "db"])

View File

@ -165,10 +165,7 @@ these changes.
* The ``syncdb`` command will be removed. * The ``syncdb`` command will be removed.
* ``django.db.models.signals.pre_syncdb`` and * ``django.db.models.signals.pre_syncdb`` and
``django.db.models.signals.post_syncdb`` will be removed, and ``django.db.models.signals.post_syncdb`` will be removed.
``django.db.models.signals.pre_migrate`` and
``django.db.models.signals.post_migrate`` will lose their
``create_models`` and ``created_models`` arguments.
* ``allow_syncdb`` on database routers will no longer automatically become * ``allow_syncdb`` on database routers will no longer automatically become
``allow_migrate``. ``allow_migrate``.

View File

@ -381,12 +381,10 @@ handlers are registered anywhere else they may not be loaded by
Arguments sent with this signal: Arguments sent with this signal:
``sender`` ``sender``
The ``models`` module of the app about to be migrated/synced. An :class:`~django.apps.AppConfig` instance for the application about to
For example, if :djadmin:`migrate` is about to install be migrated/synced.
an app called ``"foo.bar.myapp"``, ``sender`` will be the
``foo.bar.myapp.models`` module.
``app`` ``app_config``
Same as ``sender``. Same as ``sender``.
``verbosity`` ``verbosity``
@ -415,14 +413,48 @@ pre_syncdb
.. deprecated:: 1.7 .. deprecated:: 1.7
This signal has been renamed to :data:`~django.db.models.signals.pre_migrate`. This signal has been replaced by :data:`~django.db.models.signals.pre_migrate`.
Alias of :data:`django.db.models.signals.pre_migrate`. As long as this alias Sent by the :djadmin:`syncdb` command before it starts to install an
is present, for backwards-compatibility this signal has an extra argument it sends: 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`.
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.
``app``
Same as ``sender``.
``create_models`` ``create_models``
A list of the model classes from any app which :djadmin:`migrate` is A list of the model classes from any app which :djadmin:`syncdb` plans to
going to create, **only if the app has no migrations**. 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
output to the screen based on the value of this argument.
``interactive``
If ``interactive`` is ``True``, it's safe to prompt the user to input
things on the command line. If ``interactive`` is ``False``, functions
which listen for this signal should not try to prompt for anything.
For example, the :mod:`django.contrib.auth` app only prompts to create a
superuser when ``interactive`` is ``True``.
``db``
The alias of database on which a command will operate.
post_migrate post_migrate
------------ ------------
@ -444,11 +476,10 @@ idempotent changes (e.g. no database alterations) as this may cause the
Arguments sent with this signal: Arguments sent with this signal:
``sender`` ``sender``
The ``models`` module that was just installed. That is, if An :class:`~django.apps.AppConfig` instance for the application that was
:djadmin:`migrate` just installed an app called ``"foo.bar.myapp"``, just installed.
``sender`` will be the ``foo.bar.myapp.models`` module.
``app`` ``app_config``
Same as ``sender``. Same as ``sender``.
``verbosity`` ``verbosity``
@ -489,14 +520,62 @@ post_syncdb
.. deprecated:: 1.7 .. deprecated:: 1.7
This signal has been renamed to :data:`~django.db.models.signals.post_migrate`. This signal has been replaced by :data:`~django.db.models.signals.post_migrate`.
Alias of :data:`django.db.models.signals.post_migrate`. As long as this alias Sent by the :djadmin:`syncdb` command after it installs an application, and the
is present, for backwards-compatibility this signal has an extra argument it sends: :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
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.
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.
``app``
Same as ``sender``.
``created_models`` ``created_models``
A list of the model classes from any app which :djadmin:`migrate` has A list of the model classes from any app which :djadmin:`syncdb` has
created, **only if the app has no migrations**. 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
output to the screen based on the value of this argument.
``interactive``
If ``interactive`` is ``True``, it's safe to prompt the user to input
things on the command line. If ``interactive`` is ``False``, functions
which listen for this signal should not try to prompt for anything.
For example, the :mod:`django.contrib.auth` app only prompts to create a
superuser when ``interactive`` is ``True``.
``db``
The database alias used for synchronization. Defaults to the ``default``
database.
For example, ``yourapp/management/__init__.py`` could be written like::
from django.db.models.signals import post_syncdb
import yourapp.models
def my_callback(sender, **kwargs):
# Your specific logic here
pass
post_syncdb.connect(my_callback, sender=yourapp.models)
Request/response signals Request/response signals
======================== ========================

View File

@ -47,11 +47,12 @@ but a few of the key features are:
* A new ``makemigrations`` command provides an easy way to autodetect changes * A new ``makemigrations`` command provides an easy way to autodetect changes
to your models and make migrations for them. to your models and make migrations for them.
* :data:`~django.db.models.signals.pre_syncdb` and :data:`~django.db.models.signals.pre_syncdb` and
:data:`~django.db.models.signals.post_syncdb` have been renamed to :data:`~django.db.models.signals.post_syncdb` have been replaced by
:data:`~django.db.models.signals.pre_migrate` and :data:`~django.db.models.signals.pre_migrate` and
:data:`~django.db.models.signals.post_migrate` respectively. The :data:`~django.db.models.signals.post_migrate` respectively. These new
``create_models``/``created_models`` argument has also been deprecated. signals have slightly different arguments. Check the documentation for
details.
* The ``allow_syncdb`` method on database routers is now called ``allow_migrate``, * The ``allow_syncdb`` method on database routers is now called ``allow_migrate``,
but still performs the same function. Routers with ``allow_syncdb`` methods but still performs the same function. Routers with ``allow_syncdb`` methods

View File

@ -1,2 +1,2 @@
# Remove this module when pre/post_migrate are refactored to use something # This module has to exist, otherwise pre/post_migrate aren't sent for the
# other than a models module for their "sender" argument. # migrate_signals application.

View File

@ -1,12 +1,12 @@
from django.apps import apps
from django.db.models import signals from django.db.models import signals
from django.test import TestCase from django.test import TestCase
from django.core import management from django.core import management
from django.utils import six from django.utils import six
from . import models
APP_CONFIG = apps.get_app_config('migrate_signals')
PRE_MIGRATE_ARGS = ['app', 'create_models', 'verbosity', 'interactive', 'db'] PRE_MIGRATE_ARGS = ['app_config', 'verbosity', 'interactive', 'db']
MIGRATE_DATABASE = 'default' MIGRATE_DATABASE = 'default'
MIGRATE_VERBOSITY = 1 MIGRATE_VERBOSITY = 1
MIGRATE_INTERACTIVE = False MIGRATE_INTERACTIVE = False
@ -39,7 +39,7 @@ class OneTimeReceiver(object):
self.call_counter = self.call_counter + 1 self.call_counter = self.call_counter + 1
self.call_args = kwargs self.call_args = kwargs
# we need to test only one call of migrate # we need to test only one call of migrate
signals.pre_migrate.disconnect(pre_migrate_receiver, sender=models) signals.pre_migrate.disconnect(pre_migrate_receiver, sender=APP_CONFIG)
# We connect receiver here and not in unit test code because we need to # We connect receiver here and not in unit test code because we need to
@ -51,21 +51,19 @@ class OneTimeReceiver(object):
# 3. Test runner calls migrate for create default database. # 3. Test runner calls migrate for create default database.
# 4. Test runner execute our unit test code. # 4. Test runner execute our unit test code.
pre_migrate_receiver = OneTimeReceiver() pre_migrate_receiver = OneTimeReceiver()
signals.pre_migrate.connect(pre_migrate_receiver, sender=models) signals.pre_migrate.connect(pre_migrate_receiver, sender=APP_CONFIG)
class MigrateSignalTests(TestCase): class MigrateSignalTests(TestCase):
available_apps = [ available_apps = ['migrate_signals']
'migrate_signals',
]
def test_pre_migrate_call_time(self): def test_pre_migrate_call_time(self):
self.assertEqual(pre_migrate_receiver.call_counter, 1) self.assertEqual(pre_migrate_receiver.call_counter, 1)
def test_pre_migrate_args(self): def test_pre_migrate_args(self):
r = PreMigrateReceiver() r = PreMigrateReceiver()
signals.pre_migrate.connect(r, sender=models) signals.pre_migrate.connect(r, sender=APP_CONFIG)
management.call_command('migrate', database=MIGRATE_DATABASE, management.call_command('migrate', database=MIGRATE_DATABASE,
verbosity=MIGRATE_VERBOSITY, interactive=MIGRATE_INTERACTIVE, verbosity=MIGRATE_VERBOSITY, interactive=MIGRATE_INTERACTIVE,
load_initial_data=False, stdout=six.StringIO()) load_initial_data=False, stdout=six.StringIO())
@ -73,7 +71,7 @@ class MigrateSignalTests(TestCase):
args = r.call_args args = r.call_args
self.assertEqual(r.call_counter, 1) self.assertEqual(r.call_counter, 1)
self.assertEqual(set(args), set(PRE_MIGRATE_ARGS)) self.assertEqual(set(args), set(PRE_MIGRATE_ARGS))
self.assertEqual(args['app'], models) self.assertEqual(args['app_config'], APP_CONFIG)
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['db'], 'default') self.assertEqual(args['db'], 'default')