Commit c8985a8a authored by Russell Keith-Magee's avatar Russell Keith-Magee
Browse files

Fixed #19806 -- Ensure that content types and permissions aren't created for swapped models.

Thanks to rizumu for the report.
parent 0a0a0d66
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ from django.utils.encoding import smart_text
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):
    """
    Creates content types for models in the given app, removing any model
@@ -77,6 +78,7 @@ If you're unsure, answer 'no'.
            if verbosity >= 2:
                print("Stale content types remain.")


def update_all_contenttypes(verbosity=2, **kwargs):
    for app in get_apps():
        update_contenttypes(app, None, verbosity, **kwargs)
+17 −11
Original line number Diff line number Diff line
@@ -35,7 +35,10 @@ def get_validation_errors(outfile, app=None):
    for (app_name, error) in get_app_errors().items():
        e.add(app_name, error)

    for cls in models.get_models(app):
    inc = set(models.get_models(app, include_swapped=True))
    no_inc = set(models.get_models(app))

    for cls in models.get_models(app, include_swapped=True):
        opts = cls._meta

        # Check swappable attribute.
@@ -138,16 +141,17 @@ def get_validation_errors(outfile, app=None):
            # fields, m2m fields, m2m related objects or related objects
            if f.rel:
                if f.rel.to not in models.get_models():
                    # If the related model is swapped, provide a hint;
                    # otherwise, the model just hasn't been installed.
                    if not isinstance(f.rel.to, six.string_types) and f.rel.to._meta.swapped:
                        e.add(opts, "'%s' defines a relation with the model '%s.%s', which has been swapped out. Update the relation to point at settings.%s." % (f.name, f.rel.to._meta.app_label, f.rel.to._meta.object_name, f.rel.to._meta.swappable))
                    else:
                        e.add(opts, "'%s' has a relation with model %s, which has either not been installed or is abstract." % (f.name, f.rel.to))
                # it is a string and we could not find the model it refers to
                # so skip the next section
                if isinstance(f.rel.to, six.string_types):
                    continue

                # Make sure the model we're related hasn't been swapped out
                if f.rel.to._meta.swapped:
                    e.add(opts, "'%s' defines a relation with the model '%s.%s', which has been swapped out. Update the relation to point at settings.%s." % (f.name, f.rel.to._meta.app_label, f.rel.to._meta.object_name, f.rel.to._meta.swappable))

                # Make sure the related field specified by a ForeignKey is unique
                if not f.rel.to._meta.get_field(f.rel.field_name).unique:
                    e.add(opts, "Field '%s' under model '%s' must have a unique=True constraint." % (f.rel.field_name, f.rel.to.__name__))
@@ -184,16 +188,18 @@ def get_validation_errors(outfile, app=None):
            # existing fields, m2m fields, m2m related objects or related
            # objects
            if f.rel.to not in models.get_models():
                # If the related model is swapped, provide a hint;
                # otherwise, the model just hasn't been installed.
                if not isinstance(f.rel.to, six.string_types) and f.rel.to._meta.swapped:
                    e.add(opts, "'%s' defines a relation with the model '%s.%s', which has been swapped out. Update the relation to point at settings.%s." % (f.name, f.rel.to._meta.app_label, f.rel.to._meta.object_name, f.rel.to._meta.swappable))
                else:
                    e.add(opts, "'%s' has an m2m relation with model %s, which has either not been installed or is abstract." % (f.name, f.rel.to))

                # it is a string and we could not find the model it refers to
                # so skip the next section
                if isinstance(f.rel.to, six.string_types):
                    continue

            # Make sure the model we're related hasn't been swapped out
            if f.rel.to._meta.swapped:
                e.add(opts, "'%s' defines a relation with the model '%s.%s', which has been swapped out. Update the relation to point at settings.%s." % (f.name, f.rel.to._meta.app_label, f.rel.to._meta.object_name, f.rel.to._meta.swappable))

            # Check that the field is not set to unique.  ManyToManyFields do not support unique.
            if f.unique:
                e.add(opts, "ManyToManyFields cannot be unique.  Remove the unique argument on '%s'." % f.name)
+21 −12
Original line number Diff line number Diff line
@@ -167,7 +167,7 @@ class AppCache(object):

    def get_models(self, app_mod=None,
                   include_auto_created=False, include_deferred=False,
                   only_installed=True):
                   only_installed=True, include_swapped=False):
        """
        Given a module containing models, returns a list of the models.
        Otherwise returns a list of all installed models.
@@ -179,8 +179,16 @@ class AppCache(object):
        By default, models created to satisfy deferred attribute
        queries are *not* included in the list of models. However, if
        you specify include_deferred, they will be.

        By default, models that aren't part of installed apps will *not*
        be included in the list of models. However, if you specify
        only_installed=False, they will be.

        By default, models that have been swapped out will *not* be
        included in the list of models. However, if you specify
        include_swapped, they will be.
        """
        cache_key = (app_mod, include_auto_created, include_deferred, only_installed)
        cache_key = (app_mod, include_auto_created, include_deferred, only_installed, include_swapped)
        try:
            return self._get_models_cache[cache_key]
        except KeyError:
@@ -203,7 +211,8 @@ class AppCache(object):
            model_list.extend(
                model for model in app.values()
                if ((not model._deferred or include_deferred) and
                    (not model._meta.auto_created or include_auto_created))
                    (not model._meta.auto_created or include_auto_created) and
                    (not model._meta.swapped or include_swapped))
            )
        self._get_models_cache[cache_key] = model_list
        return model_list
+0 −0

Empty file added.

+15 −0
Original line number Diff line number Diff line
from django.db import models


class Article(models.Model):
    title =  models.CharField(max_length=100)
    publication_date = models.DateField()

    class Meta:
        swappable = 'TEST_ARTICLE_MODEL'


class AlternateArticle(models.Model):
    title =  models.CharField(max_length=100)
    publication_date = models.DateField()
    byline = models.CharField(max_length=100)
Loading