浏览代码

Fixed #26085 -- Fixed contenttypes shortcut() view crash with a null fk to Site.

Thanks Fabien Schwob for the initial patch.
dani poni 9 年之前
父节点
当前提交
d29d11b026
共有 3 个文件被更改,包括 46 次插入4 次删除
  1. 4 2
      django/contrib/contenttypes/views.py
  2. 22 0
      tests/contenttypes_tests/models.py
  3. 20 2
      tests/contenttypes_tests/tests.py

+ 4 - 2
django/contrib/contenttypes/views.py

@@ -63,9 +63,11 @@ def shortcut(request, content_type_id, object_id):
             for field in obj._meta.fields:
                 if field.remote_field and field.remote_field.model is Site:
                     try:
-                        object_domain = getattr(obj, field.name).domain
+                        site = getattr(obj, field.name)
                     except Site.DoesNotExist:
-                        pass
+                        continue
+                    if site is not None:
+                        object_domain = site.domain
                     if object_domain is not None:
                         break
 

+ 22 - 0
tests/contenttypes_tests/models.py

@@ -4,11 +4,21 @@ from django.contrib.contenttypes.fields import (
     GenericForeignKey, GenericRelation,
 )
 from django.contrib.contenttypes.models import ContentType
+from django.contrib.sites.models import SiteManager
 from django.db import models
 from django.utils.encoding import python_2_unicode_compatible
 from django.utils.http import urlquote
 
 
+@python_2_unicode_compatible
+class Site(models.Model):
+    domain = models.CharField(max_length=100)
+    objects = SiteManager()
+
+    def __str__(self):
+        return self.domain
+
+
 @python_2_unicode_compatible
 class Author(models.Model):
     name = models.CharField(max_length=100)
@@ -115,3 +125,15 @@ class Post(models.Model):
 
     def __str__(self):
         return self.title
+
+
+@python_2_unicode_compatible
+class ModelWithNullFKToSite(models.Model):
+    title = models.CharField(max_length=200)
+    site = models.ForeignKey(Site, null=True, on_delete=models.CASCADE)
+
+    def __str__(self):
+        return self.title
+
+    def get_absolute_url(self):
+        return '/title/%s/' % urlquote(self.title)

+ 20 - 2
tests/contenttypes_tests/tests.py

@@ -12,11 +12,14 @@ from django.contrib.contenttypes.models import ContentType
 from django.contrib.sites.models import Site
 from django.core import checks
 from django.db import connections, models
-from django.test import SimpleTestCase, TestCase, override_settings
+from django.test import SimpleTestCase, TestCase, mock, override_settings
 from django.test.utils import captured_stdout, isolate_apps
 from django.utils.encoding import force_str, force_text
 
-from .models import Article, Author, SchemeIncludedURL
+from .models import (
+    Article, Author, ModelWithNullFKToSite, SchemeIncludedURL,
+    Site as MockSite,
+)
 
 
 @override_settings(ROOT_URLCONF='contenttypes_tests.urls')
@@ -94,6 +97,21 @@ class ContentTypesViewsTests(TestCase):
         response = self.client.get(short_url)
         self.assertEqual(response.status_code, 404)
 
+    @mock.patch('django.apps.apps.get_model')
+    def test_shortcut_view_with_null_site_fk(self, get_model):
+        """
+        The shortcut view works if a model's ForeignKey to site is None.
+        """
+        get_model.side_effect = lambda *args, **kwargs: MockSite if args[0] == 'sites.Site' else ModelWithNullFKToSite
+
+        obj = ModelWithNullFKToSite.objects.create(title='title')
+        url = '/shortcut/%s/%s/' % (ContentType.objects.get_for_model(ModelWithNullFKToSite).id, obj.pk)
+        response = self.client.get(url)
+        self.assertRedirects(
+            response, '%s' % obj.get_absolute_url(),
+            fetch_redirect_response=False,
+        )
+
     def test_create_contenttype_on_the_spot(self):
         """
         Make sure ContentTypeManager.get_for_model creates the corresponding