Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[1.5.x] Fixed #19401 -- Ensure that swappable model references are ca…

…se 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.

Backport of c04c03d from trunk.
  • Loading branch information...
commit b7607003a543b11c62ad4d2ca1f8e12bd4b97b89 1 parent 9892919
Russell Keith-Magee authored
19  django/db/models/options.py
@@ -231,12 +231,25 @@ def _swapped(self):
231 231
         """
232 232
         Has this model been swapped out for another? If so, return the model
233 233
         name of the replacement; otherwise, return None.
  234
+
  235
+        For historical reasons, model name lookups using get_model() are
  236
+        case insensitive, so we make sure we are case insensitive here.
234 237
         """
235 238
         if self.swappable:
236  
-            model_label = '%s.%s' % (self.app_label, self.object_name)
  239
+            model_label = '%s.%s' % (self.app_label, self.object_name.lower())
237 240
             swapped_for = getattr(settings, self.swappable, None)
238  
-            if swapped_for not in (None, model_label):
239  
-                return swapped_for
  241
+            if swapped_for:
  242
+                try:
  243
+                    swapped_label, swapped_object = swapped_for.split('.')
  244
+                except ValueError:
  245
+                    # setting not in the format app_label.model_name
  246
+                    # raising ImproperlyConfigured here causes problems with
  247
+                    # test cleanup code - instead it is raised in get_user_model
  248
+                    # or as part of validation.
  249
+                    return swapped_for
  250
+
  251
+                if '%s.%s' % (swapped_label, swapped_object.lower()) not in (None, model_label):
  252
+                    return swapped_for
240 253
         return None
241 254
     swapped = property(_swapped)
242 255
 
12  tests/regressiontests/swappable_models/tests.py
@@ -9,6 +9,8 @@
9 9
 from django.test import TestCase
10 10
 from django.test.utils import override_settings
11 11
 
  12
+from regressiontests.swappable_models.models import Article
  13
+
12 14
 
13 15
 class SwappableModelTests(TestCase):
14 16
     def setUp(self):
@@ -44,3 +46,13 @@ def test_generated_data(self):
44 46
                        for ct in ContentType.objects.all()]
45 47
         self.assertIn(('swappable_models', 'alternatearticle'), apps_models)
46 48
         self.assertNotIn(('swappable_models', 'article'), apps_models)
  49
+
  50
+    @override_settings(TEST_ARTICLE_MODEL='swappable_models.article')
  51
+    def test_case_insensitive(self):
  52
+        "Model names are case insensitive. Check that model swapping honors this."
  53
+        try:
  54
+            Article.objects.all()
  55
+        except AttributeError:
  56
+            self.fail('Swappable model names should be case insensitive.')
  57
+
  58
+        self.assertIsNone(Article._meta.swapped)

0 notes on commit b760700

Please sign in to comment.
Something went wrong with that request. Please try again.