From c04c03daa3620dc5106740a976738ada35203ab5 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Thu, 20 Dec 2012 16:10:19 +0800 Subject: [PATCH] Fixed #19401 -- Ensure that swappable model references are case insensitive. This is necessary because get_model() checks are case insensitive, and if the swapable check isn't, the swappable logic gets tied up in knots with models that are partially swapped out. Thanks to chris@cogdon.org for the report and extensive analysis, and Preston for his work on the draft patch. --- django/db/models/options.py | 19 ++++++++++++++++--- .../regressiontests/swappable_models/tests.py | 12 ++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/django/db/models/options.py b/django/db/models/options.py index f74d9ffff8..45b32b0ea4 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -213,12 +213,25 @@ class Options(object): """ Has this model been swapped out for another? If so, return the model name of the replacement; otherwise, return None. + + For historical reasons, model name lookups using get_model() are + case insensitive, so we make sure we are case insensitive here. """ if self.swappable: - model_label = '%s.%s' % (self.app_label, self.object_name) + model_label = '%s.%s' % (self.app_label, self.object_name.lower()) swapped_for = getattr(settings, self.swappable, None) - if swapped_for not in (None, model_label): - return swapped_for + if swapped_for: + try: + swapped_label, swapped_object = swapped_for.split('.') + except ValueError: + # setting not in the format app_label.model_name + # raising ImproperlyConfigured here causes problems with + # test cleanup code - instead it is raised in get_user_model + # or as part of validation. + return swapped_for + + if '%s.%s' % (swapped_label, swapped_object.lower()) not in (None, model_label): + return swapped_for return None swapped = property(_swapped) diff --git a/tests/regressiontests/swappable_models/tests.py b/tests/regressiontests/swappable_models/tests.py index 75644e32aa..089b391416 100644 --- a/tests/regressiontests/swappable_models/tests.py +++ b/tests/regressiontests/swappable_models/tests.py @@ -9,6 +9,8 @@ from django.db.models.loading import cache from django.test import TestCase from django.test.utils import override_settings +from regressiontests.swappable_models.models import Article + class SwappableModelTests(TestCase): def setUp(self): @@ -44,3 +46,13 @@ class SwappableModelTests(TestCase): for ct in ContentType.objects.all()] self.assertIn(('swappable_models', 'alternatearticle'), apps_models) self.assertNotIn(('swappable_models', 'article'), apps_models) + + @override_settings(TEST_ARTICLE_MODEL='swappable_models.article') + def test_case_insensitive(self): + "Model names are case insensitive. Check that model swapping honors this." + try: + Article.objects.all() + except AttributeError: + self.fail('Swappable model names should be case insensitive.') + + self.assertIsNone(Article._meta.swapped)