tests.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. from __future__ import unicode_literals
  2. from django.db import models
  3. from django.contrib.contenttypes.models import ContentType
  4. from django.contrib.contenttypes.views import shortcut
  5. from django.contrib.sites.models import Site, get_current_site
  6. from django.http import HttpRequest, Http404
  7. from django.test import TestCase
  8. from django.test.utils import override_settings
  9. from django.utils.http import urlquote
  10. from django.utils import six
  11. from django.utils.encoding import python_2_unicode_compatible
  12. class ConcreteModel(models.Model):
  13. name = models.CharField(max_length=10)
  14. class ProxyModel(ConcreteModel):
  15. class Meta:
  16. proxy = True
  17. @python_2_unicode_compatible
  18. class FooWithoutUrl(models.Model):
  19. """
  20. Fake model not defining ``get_absolute_url`` for
  21. :meth:`ContentTypesTests.test_shortcut_view_without_get_absolute_url`"""
  22. name = models.CharField(max_length=30, unique=True)
  23. def __str__(self):
  24. return self.name
  25. class FooWithUrl(FooWithoutUrl):
  26. """
  27. Fake model defining ``get_absolute_url`` for
  28. :meth:`ContentTypesTests.test_shortcut_view`
  29. """
  30. def get_absolute_url(self):
  31. return "/users/%s/" % urlquote(self.name)
  32. class FooWithBrokenAbsoluteUrl(FooWithoutUrl):
  33. """
  34. Fake model defining a ``get_absolute_url`` method containing an error
  35. """
  36. def get_absolute_url(self):
  37. return "/users/%s/" % self.unknown_field
  38. class ContentTypesTests(TestCase):
  39. def setUp(self):
  40. self.old_Site_meta_installed = Site._meta.installed
  41. ContentType.objects.clear_cache()
  42. def tearDown(self):
  43. Site._meta.installed = self.old_Site_meta_installed
  44. ContentType.objects.clear_cache()
  45. def test_lookup_cache(self):
  46. """
  47. Make sure that the content type cache (see ContentTypeManager)
  48. works correctly. Lookups for a particular content type -- by model, ID
  49. or natural key -- should hit the database only on the first lookup.
  50. """
  51. # At this point, a lookup for a ContentType should hit the DB
  52. with self.assertNumQueries(1):
  53. ContentType.objects.get_for_model(ContentType)
  54. # A second hit, though, won't hit the DB, nor will a lookup by ID
  55. # or natural key
  56. with self.assertNumQueries(0):
  57. ct = ContentType.objects.get_for_model(ContentType)
  58. with self.assertNumQueries(0):
  59. ContentType.objects.get_for_id(ct.id)
  60. with self.assertNumQueries(0):
  61. ContentType.objects.get_by_natural_key('contenttypes',
  62. 'contenttype')
  63. # Once we clear the cache, another lookup will again hit the DB
  64. ContentType.objects.clear_cache()
  65. with self.assertNumQueries(1):
  66. ContentType.objects.get_for_model(ContentType)
  67. # The same should happen with a lookup by natural key
  68. ContentType.objects.clear_cache()
  69. with self.assertNumQueries(1):
  70. ContentType.objects.get_by_natural_key('contenttypes',
  71. 'contenttype')
  72. # And a second hit shouldn't hit the DB
  73. with self.assertNumQueries(0):
  74. ContentType.objects.get_by_natural_key('contenttypes',
  75. 'contenttype')
  76. def test_get_for_models_empty_cache(self):
  77. # Empty cache.
  78. with self.assertNumQueries(1):
  79. cts = ContentType.objects.get_for_models(ContentType, FooWithUrl)
  80. self.assertEqual(cts, {
  81. ContentType: ContentType.objects.get_for_model(ContentType),
  82. FooWithUrl: ContentType.objects.get_for_model(FooWithUrl),
  83. })
  84. def test_get_for_models_partial_cache(self):
  85. # Partial cache
  86. ContentType.objects.get_for_model(ContentType)
  87. with self.assertNumQueries(1):
  88. cts = ContentType.objects.get_for_models(ContentType, FooWithUrl)
  89. self.assertEqual(cts, {
  90. ContentType: ContentType.objects.get_for_model(ContentType),
  91. FooWithUrl: ContentType.objects.get_for_model(FooWithUrl),
  92. })
  93. def test_get_for_models_full_cache(self):
  94. # Full cache
  95. ContentType.objects.get_for_model(ContentType)
  96. ContentType.objects.get_for_model(FooWithUrl)
  97. with self.assertNumQueries(0):
  98. cts = ContentType.objects.get_for_models(ContentType, FooWithUrl)
  99. self.assertEqual(cts, {
  100. ContentType: ContentType.objects.get_for_model(ContentType),
  101. FooWithUrl: ContentType.objects.get_for_model(FooWithUrl),
  102. })
  103. def test_get_for_concrete_model(self):
  104. """
  105. Make sure the `for_concrete_model` kwarg correctly works
  106. with concrete, proxy and deferred models
  107. """
  108. concrete_model_ct = ContentType.objects.get_for_model(ConcreteModel)
  109. self.assertEqual(concrete_model_ct,
  110. ContentType.objects.get_for_model(ProxyModel))
  111. self.assertEqual(concrete_model_ct,
  112. ContentType.objects.get_for_model(ConcreteModel,
  113. for_concrete_model=False))
  114. proxy_model_ct = ContentType.objects.get_for_model(ProxyModel,
  115. for_concrete_model=False)
  116. self.assertNotEqual(concrete_model_ct, proxy_model_ct)
  117. # Make sure deferred model are correctly handled
  118. ConcreteModel.objects.create(name="Concrete")
  119. DeferredConcreteModel = ConcreteModel.objects.only('pk').get().__class__
  120. DeferredProxyModel = ProxyModel.objects.only('pk').get().__class__
  121. self.assertEqual(concrete_model_ct,
  122. ContentType.objects.get_for_model(DeferredConcreteModel))
  123. self.assertEqual(concrete_model_ct,
  124. ContentType.objects.get_for_model(DeferredConcreteModel,
  125. for_concrete_model=False))
  126. self.assertEqual(concrete_model_ct,
  127. ContentType.objects.get_for_model(DeferredProxyModel))
  128. self.assertEqual(proxy_model_ct,
  129. ContentType.objects.get_for_model(DeferredProxyModel,
  130. for_concrete_model=False))
  131. def test_get_for_concrete_models(self):
  132. """
  133. Make sure the `for_concrete_models` kwarg correctly works
  134. with concrete, proxy and deferred models.
  135. """
  136. concrete_model_ct = ContentType.objects.get_for_model(ConcreteModel)
  137. cts = ContentType.objects.get_for_models(ConcreteModel, ProxyModel)
  138. self.assertEqual(cts, {
  139. ConcreteModel: concrete_model_ct,
  140. ProxyModel: concrete_model_ct,
  141. })
  142. proxy_model_ct = ContentType.objects.get_for_model(ProxyModel,
  143. for_concrete_model=False)
  144. cts = ContentType.objects.get_for_models(ConcreteModel, ProxyModel,
  145. for_concrete_models=False)
  146. self.assertEqual(cts, {
  147. ConcreteModel: concrete_model_ct,
  148. ProxyModel: proxy_model_ct,
  149. })
  150. # Make sure deferred model are correctly handled
  151. ConcreteModel.objects.create(name="Concrete")
  152. DeferredConcreteModel = ConcreteModel.objects.only('pk').get().__class__
  153. DeferredProxyModel = ProxyModel.objects.only('pk').get().__class__
  154. cts = ContentType.objects.get_for_models(DeferredConcreteModel,
  155. DeferredProxyModel)
  156. self.assertEqual(cts, {
  157. DeferredConcreteModel: concrete_model_ct,
  158. DeferredProxyModel: concrete_model_ct,
  159. })
  160. cts = ContentType.objects.get_for_models(DeferredConcreteModel,
  161. DeferredProxyModel,
  162. for_concrete_models=False)
  163. self.assertEqual(cts, {
  164. DeferredConcreteModel: concrete_model_ct,
  165. DeferredProxyModel: proxy_model_ct,
  166. })
  167. @override_settings(ALLOWED_HOSTS=['example.com'])
  168. def test_shortcut_view(self):
  169. """
  170. Check that the shortcut view (used for the admin "view on site"
  171. functionality) returns a complete URL regardless of whether the sites
  172. framework is installed
  173. """
  174. request = HttpRequest()
  175. request.META = {
  176. "SERVER_NAME": "Example.com",
  177. "SERVER_PORT": "80",
  178. }
  179. user_ct = ContentType.objects.get_for_model(FooWithUrl)
  180. obj = FooWithUrl.objects.create(name="john")
  181. if Site._meta.installed:
  182. response = shortcut(request, user_ct.id, obj.id)
  183. self.assertEqual("http://%s/users/john/" % get_current_site(request).domain,
  184. response._headers.get("location")[1])
  185. Site._meta.installed = False
  186. response = shortcut(request, user_ct.id, obj.id)
  187. self.assertEqual("http://Example.com/users/john/",
  188. response._headers.get("location")[1])
  189. def test_shortcut_view_without_get_absolute_url(self):
  190. """
  191. Check that the shortcut view (used for the admin "view on site"
  192. functionality) returns 404 when get_absolute_url is not defined.
  193. """
  194. request = HttpRequest()
  195. request.META = {
  196. "SERVER_NAME": "Example.com",
  197. "SERVER_PORT": "80",
  198. }
  199. user_ct = ContentType.objects.get_for_model(FooWithoutUrl)
  200. obj = FooWithoutUrl.objects.create(name="john")
  201. self.assertRaises(Http404, shortcut, request, user_ct.id, obj.id)
  202. def test_shortcut_view_with_broken_get_absolute_url(self):
  203. """
  204. Check that the shortcut view does not catch an AttributeError raised
  205. by the model's get_absolute_url method.
  206. Refs #8997.
  207. """
  208. request = HttpRequest()
  209. request.META = {
  210. "SERVER_NAME": "Example.com",
  211. "SERVER_PORT": "80",
  212. }
  213. user_ct = ContentType.objects.get_for_model(FooWithBrokenAbsoluteUrl)
  214. obj = FooWithBrokenAbsoluteUrl.objects.create(name="john")
  215. self.assertRaises(AttributeError, shortcut, request, user_ct.id, obj.id)
  216. def test_missing_model(self):
  217. """
  218. Ensures that displaying content types in admin (or anywhere) doesn't
  219. break on leftover content type records in the DB for which no model
  220. is defined anymore.
  221. """
  222. ct = ContentType.objects.create(
  223. name = 'Old model',
  224. app_label = 'contenttypes',
  225. model = 'OldModel',
  226. )
  227. self.assertEqual(six.text_type(ct), 'Old model')