Commit 61a16e02 authored by Simon Charette's avatar Simon Charette
Browse files

Fixed #24075 -- Used post-migration models in contrib apps receivers.

Thanks Markus and Tim for the review.
parent f937c9ec
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@ from __future__ import unicode_literals
import getpass
import unicodedata

from django.apps import apps
from django.apps import apps as global_apps
from django.contrib.auth import get_permission_codename
from django.core import exceptions
from django.db import DEFAULT_DB_ALIAS, router
@@ -37,11 +37,14 @@ def _get_builtin_permissions(opts):
    return perms


def create_permissions(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, **kwargs):
def create_permissions(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, apps=global_apps, **kwargs):
    if not app_config.models_module:
        return

    app_label = app_config.label
    try:
        app_config = apps.get_app_config(app_label)
        ContentType = apps.get_model('contenttypes', 'ContentType')
        Permission = apps.get_model('auth', 'Permission')
    except LookupError:
        return
@@ -49,8 +52,6 @@ def create_permissions(app_config, verbosity=2, interactive=True, using=DEFAULT_
    if not router.allow_migrate_model(using, Permission):
        return

    from django.contrib.contenttypes.models import ContentType

    # This will hold the permissions we're looking for as
    # (content_type, (codename, name))
    searched_perms = list()
+7 −4
Original line number Diff line number Diff line
from django.apps import apps
from django.apps import apps as global_apps
from django.db import DEFAULT_DB_ALIAS, router
from django.utils import six
from django.utils.six.moves import input


def update_contenttypes(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, **kwargs):
def update_contenttypes(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, apps=global_apps, **kwargs):
    """
    Creates content types for models in the given app, removing any model
    entries that no longer have a matching model class.
@@ -12,7 +12,9 @@ def update_contenttypes(app_config, verbosity=2, interactive=True, using=DEFAULT
    if not app_config.models_module:
        return

    app_label = app_config.label
    try:
        app_config = apps.get_app_config(app_label)
        ContentType = apps.get_model('contenttypes', 'ContentType')
    except LookupError:
        return
@@ -21,8 +23,9 @@ def update_contenttypes(app_config, verbosity=2, interactive=True, using=DEFAULT
        return

    ContentType.objects.clear_cache()

    app_label = app_config.label
    # Always clear the global content types cache.
    if apps is not global_apps:
        global_apps.get_model('contenttypes', 'ContentType').objects.clear_cache()

    app_models = {
        model._meta.model_name: model
+9 −19
Original line number Diff line number Diff line
@@ -2,7 +2,6 @@ from __future__ import unicode_literals

from django.apps import apps
from django.db import models
from django.db.utils import IntegrityError, OperationalError, ProgrammingError
from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _

@@ -48,25 +47,16 @@ class ContentTypeManager(models.Manager):
        # The ContentType entry was not found in the cache, therefore we
        # proceed to load or create it.
        try:
            try:
                # We start with get() and not get_or_create() in order to use
            # Start with get() and not get_or_create() in order to use
            # the db_for_read (see #20401).
            ct = self.get(app_label=opts.app_label, model=opts.model_name)
        except self.model.DoesNotExist:
                # Not found in the database; we proceed to create it.  This time we
            # Not found in the database; we proceed to create it. This time
            # use get_or_create to take care of any race conditions.
            ct, created = self.get_or_create(
                app_label=opts.app_label,
                model=opts.model_name,
            )
        except (OperationalError, ProgrammingError, IntegrityError):
            # It's possible to migrate a single app before contenttypes,
            # as it's not a required initial dependency (it's contrib!)
            # Have a nice error for this.
            raise RuntimeError(
                "Error creating new content types. Please make sure contenttypes "
                "is migrated before trying to migrate apps individually."
            )
        self._add_to_cache(self.db, ct)
        return ct

+2 −2
Original line number Diff line number Diff line
@@ -2,13 +2,13 @@
Creates the default Site object.
"""

from django.apps import apps
from django.apps import apps as global_apps
from django.conf import settings
from django.core.management.color import no_style
from django.db import DEFAULT_DB_ALIAS, connections, router


def create_default_site(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, **kwargs):
def create_default_site(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, apps=global_apps, **kwargs):
    try:
        Site = apps.get_model('sites', 'Site')
    except LookupError:
+19 −5
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ from django.contrib.auth.models import Group, Permission, User
from django.contrib.contenttypes.models import ContentType
from django.core.management import call_command
from django.core.management.base import CommandError
from django.db import migrations
from django.test import TestCase, mock, override_settings
from django.utils import six
from django.utils.encoding import force_str
@@ -561,11 +562,12 @@ class MultiDBCreatesuperuserTestCase(TestCase):
        self.assertEqual(user.email, 'joe@somewhere.org')


class PermissionTestCase(TestCase):
class CreatePermissionsTests(TestCase):

    def setUp(self):
        self._original_permissions = Permission._meta.permissions[:]
        self._original_default_permissions = Permission._meta.default_permissions
        self.app_config = apps.get_app_config('auth')

    def tearDown(self):
        Permission._meta.permissions = self._original_permissions
@@ -573,13 +575,11 @@ class PermissionTestCase(TestCase):
        ContentType.objects.clear_cache()

    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._meta.permissions = [
            ('my_custom_permission', 'Some permission'),
        ]
        create_permissions(auth_app_config, verbosity=0)
        create_permissions(self.app_config, verbosity=0)

        # add/change/delete permission by default + custom permission
        self.assertEqual(Permission.objects.filter(
@@ -588,9 +588,23 @@ class PermissionTestCase(TestCase):

        Permission.objects.filter(content_type=permission_content_type).delete()
        Permission._meta.default_permissions = []
        create_permissions(auth_app_config, verbosity=0)
        create_permissions(self.app_config, verbosity=0)

        # custom permission only since default permissions is empty
        self.assertEqual(Permission.objects.filter(
            content_type=permission_content_type,
        ).count(), 1)

    def test_unvailable_models(self):
        """
        #24075 - Permissions shouldn't be created or deleted if the ContentType
        or Permission models aren't available.
        """
        state = migrations.state.ProjectState()
        # Unvailable contenttypes.ContentType
        with self.assertNumQueries(0):
            create_permissions(self.app_config, verbosity=0, apps=state.apps)
        # Unvailable auth.Permission
        state = migrations.state.ProjectState(real_apps=['contenttypes'])
        with self.assertNumQueries(0):
            create_permissions(self.app_config, verbosity=0, apps=state.apps)
Loading