mirror of
https://github.com/django/django.git
synced 2025-10-31 09:41:08 +00:00
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:
@@ -60,18 +60,21 @@ def _check_permission_clashing(custom, builtin, ctype):
|
||||
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:
|
||||
apps.get_model('auth', 'Permission')
|
||||
Permission = apps.get_model('auth', 'Permission')
|
||||
except LookupError:
|
||||
return
|
||||
|
||||
if not router.allow_migrate(db, auth_app.Permission):
|
||||
if not router.allow_migrate(db, Permission):
|
||||
return
|
||||
|
||||
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
|
||||
# (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
|
||||
# looking for. We don't need to check for codenames since we already have
|
||||
# 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,
|
||||
).values_list(
|
||||
"content_type", "codename"
|
||||
))
|
||||
|
||||
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
|
||||
if (ctype.pk, codename) not in all_perms
|
||||
]
|
||||
# Validate the permissions before bulk_creation to avoid cryptic
|
||||
# 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
|
||||
for perm in perms:
|
||||
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,
|
||||
)
|
||||
)
|
||||
auth_app.Permission.objects.using(db).bulk_create(perms)
|
||||
Permission.objects.using(db).bulk_create(perms)
|
||||
if verbosity >= 2:
|
||||
for perm in perms:
|
||||
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:
|
||||
apps.get_model('auth', 'Permission')
|
||||
except LookupError:
|
||||
@@ -128,7 +131,7 @@ def create_superuser(app, created_models, verbosity, db, **kwargs):
|
||||
|
||||
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 "
|
||||
"don't have any superusers defined.\nWould you like to create one "
|
||||
"now? (yes/no): ")
|
||||
@@ -203,7 +206,9 @@ def get_default_username(check_db=True):
|
||||
return ''
|
||||
return default_username
|
||||
|
||||
|
||||
signals.post_migrate.connect(create_permissions,
|
||||
dispatch_uid="django.contrib.auth.management.create_permissions")
|
||||
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")
|
||||
|
||||
@@ -232,13 +232,15 @@ class PermissionTestCase(TestCase):
|
||||
Test that we show proper error message if we are trying to create
|
||||
duplicate permissions.
|
||||
"""
|
||||
auth_app_config = apps.get_app_config('auth')
|
||||
|
||||
# check duplicated default permission
|
||||
models.Permission._meta.permissions = [
|
||||
('change_permission', 'Can edit permission (duplicate)')]
|
||||
six.assertRaisesRegex(self, CommandError,
|
||||
"The permission codename 'change_permission' clashes with a "
|
||||
"builtin permission for model 'auth.Permission'.",
|
||||
create_permissions, models, [], verbosity=0)
|
||||
create_permissions, auth_app_config, verbosity=0)
|
||||
|
||||
# check duplicated custom permissions
|
||||
models.Permission._meta.permissions = [
|
||||
@@ -249,21 +251,23 @@ class PermissionTestCase(TestCase):
|
||||
six.assertRaisesRegex(self, CommandError,
|
||||
"The permission codename 'my_custom_permission' is duplicated for model "
|
||||
"'auth.Permission'.",
|
||||
create_permissions, models, [], verbosity=0)
|
||||
create_permissions, auth_app_config, verbosity=0)
|
||||
|
||||
# should not raise anything
|
||||
models.Permission._meta.permissions = [
|
||||
('my_custom_permission', 'Some permission'),
|
||||
('other_one', 'Some other permission'),
|
||||
]
|
||||
create_permissions(models, [], verbosity=0)
|
||||
create_permissions(auth_app_config, verbosity=0)
|
||||
|
||||
def test_default_permissions(self):
|
||||
auth_app_config = apps.get_app_config('auth')
|
||||
|
||||
permission_content_type = ContentType.objects.get_by_natural_key('auth', 'permission')
|
||||
models.Permission._meta.permissions = [
|
||||
('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
|
||||
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._meta.default_permissions = []
|
||||
create_permissions(models, [], verbosity=0)
|
||||
create_permissions(auth_app_config, verbosity=0)
|
||||
|
||||
# custom permission only since default permissions is empty
|
||||
self.assertEqual(models.Permission.objects.filter(
|
||||
@@ -280,10 +284,12 @@ class PermissionTestCase(TestCase):
|
||||
).count(), 1)
|
||||
|
||||
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')
|
||||
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"
|
||||
|
||||
six.assertRaisesRegex(self, exceptions.ValidationError,
|
||||
"The verbose_name of permission is longer than 39 characters",
|
||||
create_permissions, models, [], verbosity=0)
|
||||
create_permissions, auth_app_config, verbosity=0)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from django.apps import apps
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import DEFAULT_DB_ALIAS, router
|
||||
from django.db.models import signals
|
||||
from django.utils.encoding import smart_text
|
||||
@@ -7,13 +6,16 @@ from django.utils import six
|
||||
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
|
||||
entries that no longer have a matching model class.
|
||||
"""
|
||||
if not app_config.models_module:
|
||||
return
|
||||
|
||||
try:
|
||||
apps.get_model('contenttypes', 'ContentType')
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
except LookupError:
|
||||
return
|
||||
|
||||
@@ -21,7 +23,7 @@ def update_contenttypes(app, created_models, verbosity=2, db=DEFAULT_DB_ALIAS, *
|
||||
return
|
||||
|
||||
ContentType.objects.clear_cache()
|
||||
app_models = apps.get_models(app)
|
||||
app_models = apps.get_models(app_config.models_module)
|
||||
if not app_models:
|
||||
return
|
||||
# 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.")
|
||||
|
||||
|
||||
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):
|
||||
update_contenttypes(app_config.models_module, None, verbosity, **kwargs)
|
||||
update_contenttypes(app_config, **kwargs)
|
||||
|
||||
|
||||
signals.post_migrate.connect(update_contenttypes)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
update_all_contenttypes()
|
||||
|
||||
@@ -2,17 +2,22 @@
|
||||
Creates the default Site object.
|
||||
"""
|
||||
|
||||
from django.db.models import signals
|
||||
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.apps import apps
|
||||
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):
|
||||
# Only create the default sites in databases where Django created the table
|
||||
if Site in created_models and router.allow_migrate(db, Site):
|
||||
def create_default_site(app_config, verbosity=22, interactive=True, db=DEFAULT_DB_ALIAS, **kwargs):
|
||||
try:
|
||||
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
|
||||
# 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
|
||||
@@ -32,6 +37,7 @@ def create_default_site(app, created_models, verbosity, db, **kwargs):
|
||||
for command in sequence_sql:
|
||||
cursor.execute(command)
|
||||
|
||||
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'))
|
||||
|
||||
@@ -211,6 +211,13 @@ def emit_pre_migrate_signal(create_models, verbosity, interactive, db):
|
||||
if verbosity >= 2:
|
||||
print("Running pre-migrate handlers for application %s" % app_config.label)
|
||||
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,
|
||||
app=app_config.models_module,
|
||||
create_models=create_models,
|
||||
@@ -225,6 +232,13 @@ def emit_post_migrate_signal(created_models, verbosity, interactive, db):
|
||||
if verbosity >= 2:
|
||||
print("Running post-migrate handlers for application %s" % app_config.label)
|
||||
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,
|
||||
app=app_config.models_module,
|
||||
created_models=created_models,
|
||||
|
||||
@@ -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)
|
||||
|
||||
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"])
|
||||
post_syncdb = post_migrate
|
||||
pre_migrate = Signal(providing_args=["app_config", "verbosity", "interactive", "db"])
|
||||
post_migrate = Signal(providing_args=["app_config", "verbosity", "interactive", "db"])
|
||||
|
||||
pre_syncdb = Signal(providing_args=["app", "create_models", "verbosity", "interactive", "db"])
|
||||
post_syncdb = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive", "db"])
|
||||
|
||||
Reference in New Issue
Block a user