1. import datetime
    
  2. from unittest import mock
    
  3. 
    
  4. from django.contrib.contenttypes.models import ContentType
    
  5. from django.contrib.contenttypes.views import shortcut
    
  6. from django.contrib.sites.models import Site
    
  7. from django.contrib.sites.shortcuts import get_current_site
    
  8. from django.http import Http404, HttpRequest
    
  9. from django.test import TestCase, override_settings
    
  10. 
    
  11. from .models import (
    
  12.     Article,
    
  13.     Author,
    
  14.     FooWithBrokenAbsoluteUrl,
    
  15.     FooWithoutUrl,
    
  16.     FooWithUrl,
    
  17.     ModelWithM2MToSite,
    
  18.     ModelWithNullFKToSite,
    
  19.     SchemeIncludedURL,
    
  20. )
    
  21. from .models import Site as MockSite
    
  22. 
    
  23. 
    
  24. @override_settings(ROOT_URLCONF="contenttypes_tests.urls")
    
  25. class ContentTypesViewsTests(TestCase):
    
  26.     @classmethod
    
  27.     def setUpTestData(cls):
    
  28.         # Don't use the manager to ensure the site exists with pk=1, regardless
    
  29.         # of whether or not it already exists.
    
  30.         cls.site1 = Site(pk=1, domain="testserver", name="testserver")
    
  31.         cls.site1.save()
    
  32.         cls.author1 = Author.objects.create(name="Boris")
    
  33.         cls.article1 = Article.objects.create(
    
  34.             title="Old Article",
    
  35.             slug="old_article",
    
  36.             author=cls.author1,
    
  37.             date_created=datetime.datetime(2001, 1, 1, 21, 22, 23),
    
  38.         )
    
  39.         cls.article2 = Article.objects.create(
    
  40.             title="Current Article",
    
  41.             slug="current_article",
    
  42.             author=cls.author1,
    
  43.             date_created=datetime.datetime(2007, 9, 17, 21, 22, 23),
    
  44.         )
    
  45.         cls.article3 = Article.objects.create(
    
  46.             title="Future Article",
    
  47.             slug="future_article",
    
  48.             author=cls.author1,
    
  49.             date_created=datetime.datetime(3000, 1, 1, 21, 22, 23),
    
  50.         )
    
  51.         cls.scheme1 = SchemeIncludedURL.objects.create(
    
  52.             url="http://test_scheme_included_http/"
    
  53.         )
    
  54.         cls.scheme2 = SchemeIncludedURL.objects.create(
    
  55.             url="https://test_scheme_included_https/"
    
  56.         )
    
  57.         cls.scheme3 = SchemeIncludedURL.objects.create(
    
  58.             url="//test_default_scheme_kept/"
    
  59.         )
    
  60. 
    
  61.     def setUp(self):
    
  62.         Site.objects.clear_cache()
    
  63. 
    
  64.     def test_shortcut_with_absolute_url(self):
    
  65.         "Can view a shortcut for an Author object that has a get_absolute_url method"
    
  66.         for obj in Author.objects.all():
    
  67.             with self.subTest(obj=obj):
    
  68.                 short_url = "/shortcut/%s/%s/" % (
    
  69.                     ContentType.objects.get_for_model(Author).id,
    
  70.                     obj.pk,
    
  71.                 )
    
  72.                 response = self.client.get(short_url)
    
  73.                 self.assertRedirects(
    
  74.                     response,
    
  75.                     "http://testserver%s" % obj.get_absolute_url(),
    
  76.                     target_status_code=404,
    
  77.                 )
    
  78. 
    
  79.     def test_shortcut_with_absolute_url_including_scheme(self):
    
  80.         """
    
  81.         Can view a shortcut when object's get_absolute_url returns a full URL
    
  82.         the tested URLs are: "http://...", "https://..." and "//..."
    
  83.         """
    
  84.         for obj in SchemeIncludedURL.objects.all():
    
  85.             with self.subTest(obj=obj):
    
  86.                 short_url = "/shortcut/%s/%s/" % (
    
  87.                     ContentType.objects.get_for_model(SchemeIncludedURL).id,
    
  88.                     obj.pk,
    
  89.                 )
    
  90.                 response = self.client.get(short_url)
    
  91.                 self.assertRedirects(
    
  92.                     response, obj.get_absolute_url(), fetch_redirect_response=False
    
  93.                 )
    
  94. 
    
  95.     def test_shortcut_no_absolute_url(self):
    
  96.         """
    
  97.         Shortcuts for an object that has no get_absolute_url() method raise
    
  98.         404.
    
  99.         """
    
  100.         for obj in Article.objects.all():
    
  101.             with self.subTest(obj=obj):
    
  102.                 short_url = "/shortcut/%s/%s/" % (
    
  103.                     ContentType.objects.get_for_model(Article).id,
    
  104.                     obj.pk,
    
  105.                 )
    
  106.                 response = self.client.get(short_url)
    
  107.                 self.assertEqual(response.status_code, 404)
    
  108. 
    
  109.     def test_wrong_type_pk(self):
    
  110.         short_url = "/shortcut/%s/%s/" % (
    
  111.             ContentType.objects.get_for_model(Author).id,
    
  112.             "nobody/expects",
    
  113.         )
    
  114.         response = self.client.get(short_url)
    
  115.         self.assertEqual(response.status_code, 404)
    
  116. 
    
  117.     def test_shortcut_bad_pk(self):
    
  118.         short_url = "/shortcut/%s/%s/" % (
    
  119.             ContentType.objects.get_for_model(Author).id,
    
  120.             "42424242",
    
  121.         )
    
  122.         response = self.client.get(short_url)
    
  123.         self.assertEqual(response.status_code, 404)
    
  124. 
    
  125.     def test_nonint_content_type(self):
    
  126.         an_author = Author.objects.all()[0]
    
  127.         short_url = "/shortcut/%s/%s/" % ("spam", an_author.pk)
    
  128.         response = self.client.get(short_url)
    
  129.         self.assertEqual(response.status_code, 404)
    
  130. 
    
  131.     def test_bad_content_type(self):
    
  132.         an_author = Author.objects.all()[0]
    
  133.         short_url = "/shortcut/%s/%s/" % (42424242, an_author.pk)
    
  134.         response = self.client.get(short_url)
    
  135.         self.assertEqual(response.status_code, 404)
    
  136. 
    
  137. 
    
  138. @override_settings(ROOT_URLCONF="contenttypes_tests.urls")
    
  139. class ContentTypesViewsSiteRelTests(TestCase):
    
  140.     def setUp(self):
    
  141.         Site.objects.clear_cache()
    
  142. 
    
  143.     @classmethod
    
  144.     def setUpTestData(cls):
    
  145.         cls.site_2 = Site.objects.create(domain="example2.com", name="example2.com")
    
  146.         cls.site_3 = Site.objects.create(domain="example3.com", name="example3.com")
    
  147. 
    
  148.     @mock.patch("django.apps.apps.get_model")
    
  149.     def test_shortcut_view_with_null_site_fk(self, get_model):
    
  150.         """
    
  151.         The shortcut view works if a model's ForeignKey to site is None.
    
  152.         """
    
  153.         get_model.side_effect = (
    
  154.             lambda *args, **kwargs: MockSite
    
  155.             if args[0] == "sites.Site"
    
  156.             else ModelWithNullFKToSite
    
  157.         )
    
  158. 
    
  159.         obj = ModelWithNullFKToSite.objects.create(title="title")
    
  160.         url = "/shortcut/%s/%s/" % (
    
  161.             ContentType.objects.get_for_model(ModelWithNullFKToSite).id,
    
  162.             obj.pk,
    
  163.         )
    
  164.         response = self.client.get(url)
    
  165.         expected_url = "http://example.com%s" % obj.get_absolute_url()
    
  166.         self.assertRedirects(response, expected_url, fetch_redirect_response=False)
    
  167. 
    
  168.     @mock.patch("django.apps.apps.get_model")
    
  169.     def test_shortcut_view_with_site_m2m(self, get_model):
    
  170.         """
    
  171.         When the object has a ManyToManyField to Site, redirect to the current
    
  172.         site if it's attached to the object or to the domain of the first site
    
  173.         found in the m2m relationship.
    
  174.         """
    
  175.         get_model.side_effect = (
    
  176.             lambda *args, **kwargs: MockSite
    
  177.             if args[0] == "sites.Site"
    
  178.             else ModelWithM2MToSite
    
  179.         )
    
  180. 
    
  181.         # get_current_site() will lookup a Site object, so these must match the
    
  182.         # domains in the MockSite model.
    
  183.         MockSite.objects.bulk_create(
    
  184.             [
    
  185.                 MockSite(pk=1, domain="example.com"),
    
  186.                 MockSite(pk=self.site_2.pk, domain=self.site_2.domain),
    
  187.                 MockSite(pk=self.site_3.pk, domain=self.site_3.domain),
    
  188.             ]
    
  189.         )
    
  190.         ct = ContentType.objects.get_for_model(ModelWithM2MToSite)
    
  191.         site_3_obj = ModelWithM2MToSite.objects.create(
    
  192.             title="Not Linked to Current Site"
    
  193.         )
    
  194.         site_3_obj.sites.add(MockSite.objects.get(pk=self.site_3.pk))
    
  195.         expected_url = "http://%s%s" % (
    
  196.             self.site_3.domain,
    
  197.             site_3_obj.get_absolute_url(),
    
  198.         )
    
  199. 
    
  200.         with self.settings(SITE_ID=self.site_2.pk):
    
  201.             # Redirects to the domain of the first Site found in the m2m
    
  202.             # relationship (ordering is arbitrary).
    
  203.             response = self.client.get("/shortcut/%s/%s/" % (ct.pk, site_3_obj.pk))
    
  204.             self.assertRedirects(response, expected_url, fetch_redirect_response=False)
    
  205. 
    
  206.         obj_with_sites = ModelWithM2MToSite.objects.create(
    
  207.             title="Linked to Current Site"
    
  208.         )
    
  209.         obj_with_sites.sites.set(MockSite.objects.all())
    
  210.         shortcut_url = "/shortcut/%s/%s/" % (ct.pk, obj_with_sites.pk)
    
  211.         expected_url = "http://%s%s" % (
    
  212.             self.site_2.domain,
    
  213.             obj_with_sites.get_absolute_url(),
    
  214.         )
    
  215. 
    
  216.         with self.settings(SITE_ID=self.site_2.pk):
    
  217.             # Redirects to the domain of the Site matching the current site's
    
  218.             # domain.
    
  219.             response = self.client.get(shortcut_url)
    
  220.             self.assertRedirects(response, expected_url, fetch_redirect_response=False)
    
  221. 
    
  222.         with self.settings(SITE_ID=None, ALLOWED_HOSTS=[self.site_2.domain]):
    
  223.             # Redirects to the domain of the Site matching the request's host
    
  224.             # header.
    
  225.             response = self.client.get(shortcut_url, SERVER_NAME=self.site_2.domain)
    
  226.             self.assertRedirects(response, expected_url, fetch_redirect_response=False)
    
  227. 
    
  228. 
    
  229. class ShortcutViewTests(TestCase):
    
  230.     def setUp(self):
    
  231.         self.request = HttpRequest()
    
  232.         self.request.META = {"SERVER_NAME": "Example.com", "SERVER_PORT": "80"}
    
  233. 
    
  234.     @override_settings(ALLOWED_HOSTS=["example.com"])
    
  235.     def test_not_dependent_on_sites_app(self):
    
  236.         """
    
  237.         The view returns a complete URL regardless of whether the sites
    
  238.         framework is installed.
    
  239.         """
    
  240.         user_ct = ContentType.objects.get_for_model(FooWithUrl)
    
  241.         obj = FooWithUrl.objects.create(name="john")
    
  242.         with self.modify_settings(INSTALLED_APPS={"append": "django.contrib.sites"}):
    
  243.             response = shortcut(self.request, user_ct.id, obj.id)
    
  244.             self.assertEqual(
    
  245.                 "http://%s/users/john/" % get_current_site(self.request).domain,
    
  246.                 response.headers.get("location"),
    
  247.             )
    
  248.         with self.modify_settings(INSTALLED_APPS={"remove": "django.contrib.sites"}):
    
  249.             response = shortcut(self.request, user_ct.id, obj.id)
    
  250.             self.assertEqual(
    
  251.                 "http://Example.com/users/john/", response.headers.get("location")
    
  252.             )
    
  253. 
    
  254.     def test_model_without_get_absolute_url(self):
    
  255.         """The view returns 404 when Model.get_absolute_url() isn't defined."""
    
  256.         user_ct = ContentType.objects.get_for_model(FooWithoutUrl)
    
  257.         obj = FooWithoutUrl.objects.create(name="john")
    
  258.         with self.assertRaises(Http404):
    
  259.             shortcut(self.request, user_ct.id, obj.id)
    
  260. 
    
  261.     def test_model_with_broken_get_absolute_url(self):
    
  262.         """
    
  263.         The view doesn't catch an AttributeError raised by
    
  264.         Model.get_absolute_url() (#8997).
    
  265.         """
    
  266.         user_ct = ContentType.objects.get_for_model(FooWithBrokenAbsoluteUrl)
    
  267.         obj = FooWithBrokenAbsoluteUrl.objects.create(name="john")
    
  268.         with self.assertRaises(AttributeError):
    
  269.             shortcut(self.request, user_ct.id, obj.id)