Commit 00110904 authored by Aymeric Augustin's avatar Aymeric Augustin
Browse files

Refactored the migration signals to use app configs.

De-aliased pre/post_syncdb to pre/post_migrate to increase
backwards-compatibility.
parent 5782c94f
Loading
Loading
Loading
Loading
+16 −11
Original line number Diff line number Diff line
@@ -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")
+12 −6
Original line number Diff line number Diff line
@@ -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)
+10 −6
Original line number Diff line number Diff line
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()
+16 −10
Original line number Diff line number Diff line
@@ -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_config, verbosity=22, interactive=True, db=DEFAULT_DB_ALIAS, **kwargs):
    try:
        Site = apps.get_model('sites', 'Site')
    except LookupError:
        return

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):
    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
@@ -34,4 +39,5 @@ def create_default_site(app, created_models, verbosity, db, **kwargs):

        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'))
+14 −0
Original line number Diff line number Diff line
@@ -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,
Loading