test_edit.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. from __future__ import unicode_literals
  2. import warnings
  3. from django import forms
  4. from django.core.exceptions import ImproperlyConfigured
  5. from django.core.urlresolvers import reverse
  6. from django.test import SimpleTestCase, TestCase, override_settings
  7. from django.test.client import RequestFactory
  8. from django.utils.deprecation import RemovedInDjango110Warning
  9. from django.views.generic.base import View
  10. from django.views.generic.edit import CreateView, FormMixin, ModelFormMixin
  11. from . import views
  12. from .models import Artist, Author
  13. from .test_forms import AuthorForm
  14. class FormMixinTests(SimpleTestCase):
  15. def test_initial_data(self):
  16. """ Test instance independence of initial data dict (see #16138) """
  17. initial_1 = FormMixin().get_initial()
  18. initial_1['foo'] = 'bar'
  19. initial_2 = FormMixin().get_initial()
  20. self.assertNotEqual(initial_1, initial_2)
  21. def test_get_prefix(self):
  22. """ Test prefix can be set (see #18872) """
  23. test_string = 'test'
  24. rf = RequestFactory()
  25. get_request = rf.get('/')
  26. class TestFormMixin(FormMixin):
  27. request = get_request
  28. default_kwargs = TestFormMixin().get_form_kwargs()
  29. self.assertIsNone(default_kwargs.get('prefix'))
  30. set_mixin = TestFormMixin()
  31. set_mixin.prefix = test_string
  32. set_kwargs = set_mixin.get_form_kwargs()
  33. self.assertEqual(test_string, set_kwargs.get('prefix'))
  34. def test_get_form(self):
  35. class TestFormMixin(FormMixin):
  36. request = RequestFactory().get('/')
  37. self.assertIsInstance(
  38. TestFormMixin().get_form(forms.Form), forms.Form,
  39. 'get_form() should use provided form class.'
  40. )
  41. class FormClassTestFormMixin(TestFormMixin):
  42. form_class = forms.Form
  43. self.assertIsInstance(
  44. FormClassTestFormMixin().get_form(), forms.Form,
  45. 'get_form() should fallback to get_form_class() if none is provided.'
  46. )
  47. def test_get_form_missing_form_class_default_value(self):
  48. with warnings.catch_warnings(record=True) as w:
  49. warnings.filterwarnings('always')
  50. class MissingDefaultValue(FormMixin):
  51. request = RequestFactory().get('/')
  52. form_class = forms.Form
  53. def get_form(self, form_class):
  54. return form_class(**self.get_form_kwargs())
  55. self.assertEqual(len(w), 1)
  56. self.assertEqual(w[0].category, RemovedInDjango110Warning)
  57. self.assertEqual(
  58. str(w[0].message),
  59. '`generic_views.test_edit.MissingDefaultValue.get_form` method '
  60. 'must define a default value for its `form_class` argument.'
  61. )
  62. self.assertIsInstance(
  63. MissingDefaultValue().get_form(), forms.Form,
  64. )
  65. def test_get_context_data(self):
  66. class FormContext(FormMixin):
  67. request = RequestFactory().get('/')
  68. form_class = forms.Form
  69. self.assertIsInstance(FormContext().get_context_data()['form'], forms.Form)
  70. @override_settings(ROOT_URLCONF='generic_views.urls')
  71. class BasicFormTests(TestCase):
  72. def test_post_data(self):
  73. res = self.client.post('/contact/', {'name': "Me", 'message': "Hello"})
  74. self.assertRedirects(res, '/list/authors/')
  75. class ModelFormMixinTests(SimpleTestCase):
  76. def test_get_form(self):
  77. form_class = views.AuthorGetQuerySetFormView().get_form_class()
  78. self.assertEqual(form_class._meta.model, Author)
  79. def test_get_form_checks_for_object(self):
  80. mixin = ModelFormMixin()
  81. mixin.request = RequestFactory().get('/')
  82. self.assertEqual({'initial': {}, 'prefix': None},
  83. mixin.get_form_kwargs())
  84. @override_settings(ROOT_URLCONF='generic_views.urls')
  85. class CreateViewTests(TestCase):
  86. def test_create(self):
  87. res = self.client.get('/edit/authors/create/')
  88. self.assertEqual(res.status_code, 200)
  89. self.assertIsInstance(res.context['form'], forms.ModelForm)
  90. self.assertIsInstance(res.context['view'], View)
  91. self.assertNotIn('object', res.context)
  92. self.assertNotIn('author', res.context)
  93. self.assertTemplateUsed(res, 'generic_views/author_form.html')
  94. res = self.client.post('/edit/authors/create/',
  95. {'name': 'Randall Munroe', 'slug': 'randall-munroe'})
  96. self.assertEqual(res.status_code, 302)
  97. self.assertRedirects(res, '/list/authors/')
  98. self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe>'])
  99. def test_create_invalid(self):
  100. res = self.client.post('/edit/authors/create/',
  101. {'name': 'A' * 101, 'slug': 'randall-munroe'})
  102. self.assertEqual(res.status_code, 200)
  103. self.assertTemplateUsed(res, 'generic_views/author_form.html')
  104. self.assertEqual(len(res.context['form'].errors), 1)
  105. self.assertEqual(Author.objects.count(), 0)
  106. def test_create_with_object_url(self):
  107. res = self.client.post('/edit/artists/create/',
  108. {'name': 'Rene Magritte'})
  109. self.assertEqual(res.status_code, 302)
  110. artist = Artist.objects.get(name='Rene Magritte')
  111. self.assertRedirects(res, '/detail/artist/%d/' % artist.pk)
  112. self.assertQuerysetEqual(Artist.objects.all(), ['<Artist: Rene Magritte>'])
  113. def test_create_with_redirect(self):
  114. res = self.client.post('/edit/authors/create/redirect/',
  115. {'name': 'Randall Munroe', 'slug': 'randall-munroe'})
  116. self.assertEqual(res.status_code, 302)
  117. self.assertRedirects(res, '/edit/authors/create/')
  118. self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe>'])
  119. def test_create_with_interpolated_redirect(self):
  120. res = self.client.post(
  121. '/edit/authors/create/interpolate_redirect/',
  122. {'name': 'Randall Munroe', 'slug': 'randall-munroe'}
  123. )
  124. self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe>'])
  125. self.assertEqual(res.status_code, 302)
  126. pk = Author.objects.first().pk
  127. self.assertRedirects(res, '/edit/author/%d/update/' % pk)
  128. # Also test with escaped chars in URL
  129. res = self.client.post(
  130. '/edit/authors/create/interpolate_redirect_nonascii/',
  131. {'name': 'John Doe', 'slug': 'john-doe'}
  132. )
  133. self.assertEqual(res.status_code, 302)
  134. pk = Author.objects.get(name='John Doe').pk
  135. self.assertRedirects(res, '/%C3%A9dit/author/{}/update/'.format(pk))
  136. def test_create_with_special_properties(self):
  137. res = self.client.get('/edit/authors/create/special/')
  138. self.assertEqual(res.status_code, 200)
  139. self.assertIsInstance(res.context['form'], views.AuthorForm)
  140. self.assertNotIn('object', res.context)
  141. self.assertNotIn('author', res.context)
  142. self.assertTemplateUsed(res, 'generic_views/form.html')
  143. res = self.client.post('/edit/authors/create/special/',
  144. {'name': 'Randall Munroe', 'slug': 'randall-munroe'})
  145. self.assertEqual(res.status_code, 302)
  146. obj = Author.objects.get(slug='randall-munroe')
  147. self.assertRedirects(res, reverse('author_detail', kwargs={'pk': obj.pk}))
  148. self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe>'])
  149. def test_create_without_redirect(self):
  150. try:
  151. self.client.post('/edit/authors/create/naive/',
  152. {'name': 'Randall Munroe', 'slug': 'randall-munroe'})
  153. self.fail('Should raise exception -- No redirect URL provided, and no get_absolute_url provided')
  154. except ImproperlyConfigured:
  155. pass
  156. def test_create_restricted(self):
  157. res = self.client.post('/edit/authors/create/restricted/',
  158. {'name': 'Randall Munroe', 'slug': 'randall-munroe'})
  159. self.assertEqual(res.status_code, 302)
  160. self.assertRedirects(res, '/accounts/login/?next=/edit/authors/create/restricted/')
  161. def test_create_view_with_restricted_fields(self):
  162. class MyCreateView(CreateView):
  163. model = Author
  164. fields = ['name']
  165. self.assertEqual(list(MyCreateView().get_form_class().base_fields),
  166. ['name'])
  167. def test_create_view_all_fields(self):
  168. class MyCreateView(CreateView):
  169. model = Author
  170. fields = '__all__'
  171. self.assertEqual(list(MyCreateView().get_form_class().base_fields),
  172. ['name', 'slug'])
  173. def test_create_view_without_explicit_fields(self):
  174. class MyCreateView(CreateView):
  175. model = Author
  176. message = (
  177. "Using ModelFormMixin (base class of MyCreateView) without the "
  178. "'fields' attribute is prohibited."
  179. )
  180. with self.assertRaisesMessage(ImproperlyConfigured, message):
  181. MyCreateView().get_form_class()
  182. def test_define_both_fields_and_form_class(self):
  183. class MyCreateView(CreateView):
  184. model = Author
  185. form_class = AuthorForm
  186. fields = ['name']
  187. message = "Specifying both 'fields' and 'form_class' is not permitted."
  188. with self.assertRaisesMessage(ImproperlyConfigured, message):
  189. MyCreateView().get_form_class()
  190. @override_settings(ROOT_URLCONF='generic_views.urls')
  191. class UpdateViewTests(TestCase):
  192. def test_update_post(self):
  193. a = Author.objects.create(
  194. name='Randall Munroe',
  195. slug='randall-munroe',
  196. )
  197. res = self.client.get('/edit/author/%d/update/' % a.pk)
  198. self.assertEqual(res.status_code, 200)
  199. self.assertIsInstance(res.context['form'], forms.ModelForm)
  200. self.assertEqual(res.context['object'], Author.objects.get(pk=a.pk))
  201. self.assertEqual(res.context['author'], Author.objects.get(pk=a.pk))
  202. self.assertTemplateUsed(res, 'generic_views/author_form.html')
  203. # Modification with both POST and PUT (browser compatible)
  204. res = self.client.post('/edit/author/%d/update/' % a.pk,
  205. {'name': 'Randall Munroe (xkcd)', 'slug': 'randall-munroe'})
  206. self.assertEqual(res.status_code, 302)
  207. self.assertRedirects(res, '/list/authors/')
  208. self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe (xkcd)>'])
  209. def test_update_invalid(self):
  210. a = Author.objects.create(
  211. name='Randall Munroe',
  212. slug='randall-munroe',
  213. )
  214. res = self.client.post('/edit/author/%d/update/' % a.pk,
  215. {'name': 'A' * 101, 'slug': 'randall-munroe'})
  216. self.assertEqual(res.status_code, 200)
  217. self.assertTemplateUsed(res, 'generic_views/author_form.html')
  218. self.assertEqual(len(res.context['form'].errors), 1)
  219. self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe>'])
  220. def test_update_with_object_url(self):
  221. a = Artist.objects.create(name='Rene Magritte')
  222. res = self.client.post('/edit/artists/%d/update/' % a.pk,
  223. {'name': 'Rene Magritte'})
  224. self.assertEqual(res.status_code, 302)
  225. self.assertRedirects(res, '/detail/artist/%d/' % a.pk)
  226. self.assertQuerysetEqual(Artist.objects.all(), ['<Artist: Rene Magritte>'])
  227. def test_update_with_redirect(self):
  228. a = Author.objects.create(
  229. name='Randall Munroe',
  230. slug='randall-munroe',
  231. )
  232. res = self.client.post('/edit/author/%d/update/redirect/' % a.pk,
  233. {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'})
  234. self.assertEqual(res.status_code, 302)
  235. self.assertRedirects(res, '/edit/authors/create/')
  236. self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe (author of xkcd)>'])
  237. def test_update_with_interpolated_redirect(self):
  238. a = Author.objects.create(
  239. name='Randall Munroe',
  240. slug='randall-munroe',
  241. )
  242. res = self.client.post(
  243. '/edit/author/%d/update/interpolate_redirect/' % a.pk,
  244. {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'}
  245. )
  246. self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe (author of xkcd)>'])
  247. self.assertEqual(res.status_code, 302)
  248. pk = Author.objects.first().pk
  249. self.assertRedirects(res, '/edit/author/%d/update/' % pk)
  250. # Also test with escaped chars in URL
  251. res = self.client.post(
  252. '/edit/author/%d/update/interpolate_redirect_nonascii/' % a.pk,
  253. {'name': 'John Doe', 'slug': 'john-doe'}
  254. )
  255. self.assertEqual(res.status_code, 302)
  256. pk = Author.objects.get(name='John Doe').pk
  257. self.assertRedirects(res, '/%C3%A9dit/author/{}/update/'.format(pk))
  258. def test_update_with_special_properties(self):
  259. a = Author.objects.create(
  260. name='Randall Munroe',
  261. slug='randall-munroe',
  262. )
  263. res = self.client.get('/edit/author/%d/update/special/' % a.pk)
  264. self.assertEqual(res.status_code, 200)
  265. self.assertIsInstance(res.context['form'], views.AuthorForm)
  266. self.assertEqual(res.context['object'], Author.objects.get(pk=a.pk))
  267. self.assertEqual(res.context['thingy'], Author.objects.get(pk=a.pk))
  268. self.assertNotIn('author', res.context)
  269. self.assertTemplateUsed(res, 'generic_views/form.html')
  270. res = self.client.post('/edit/author/%d/update/special/' % a.pk,
  271. {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'})
  272. self.assertEqual(res.status_code, 302)
  273. self.assertRedirects(res, '/detail/author/%d/' % a.pk)
  274. self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe (author of xkcd)>'])
  275. def test_update_without_redirect(self):
  276. a = Author.objects.create(
  277. name='Randall Munroe',
  278. slug='randall-munroe',
  279. )
  280. # Should raise exception -- No redirect URL provided, and no
  281. # get_absolute_url provided
  282. with self.assertRaises(ImproperlyConfigured):
  283. self.client.post('/edit/author/%d/update/naive/' % a.pk,
  284. {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'})
  285. def test_update_get_object(self):
  286. a = Author.objects.create(
  287. pk=1,
  288. name='Randall Munroe',
  289. slug='randall-munroe',
  290. )
  291. res = self.client.get('/edit/author/update/')
  292. self.assertEqual(res.status_code, 200)
  293. self.assertIsInstance(res.context['form'], forms.ModelForm)
  294. self.assertIsInstance(res.context['view'], View)
  295. self.assertEqual(res.context['object'], Author.objects.get(pk=a.pk))
  296. self.assertEqual(res.context['author'], Author.objects.get(pk=a.pk))
  297. self.assertTemplateUsed(res, 'generic_views/author_form.html')
  298. # Modification with both POST and PUT (browser compatible)
  299. res = self.client.post('/edit/author/update/',
  300. {'name': 'Randall Munroe (xkcd)', 'slug': 'randall-munroe'})
  301. self.assertEqual(res.status_code, 302)
  302. self.assertRedirects(res, '/list/authors/')
  303. self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe (xkcd)>'])
  304. @override_settings(ROOT_URLCONF='generic_views.urls')
  305. class DeleteViewTests(TestCase):
  306. def test_delete_by_post(self):
  307. a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'})
  308. res = self.client.get('/edit/author/%d/delete/' % a.pk)
  309. self.assertEqual(res.status_code, 200)
  310. self.assertEqual(res.context['object'], Author.objects.get(pk=a.pk))
  311. self.assertEqual(res.context['author'], Author.objects.get(pk=a.pk))
  312. self.assertTemplateUsed(res, 'generic_views/author_confirm_delete.html')
  313. # Deletion with POST
  314. res = self.client.post('/edit/author/%d/delete/' % a.pk)
  315. self.assertEqual(res.status_code, 302)
  316. self.assertRedirects(res, '/list/authors/')
  317. self.assertQuerysetEqual(Author.objects.all(), [])
  318. def test_delete_by_delete(self):
  319. # Deletion with browser compatible DELETE method
  320. a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'})
  321. res = self.client.delete('/edit/author/%d/delete/' % a.pk)
  322. self.assertEqual(res.status_code, 302)
  323. self.assertRedirects(res, '/list/authors/')
  324. self.assertQuerysetEqual(Author.objects.all(), [])
  325. def test_delete_with_redirect(self):
  326. a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'})
  327. res = self.client.post('/edit/author/%d/delete/redirect/' % a.pk)
  328. self.assertEqual(res.status_code, 302)
  329. self.assertRedirects(res, '/edit/authors/create/')
  330. self.assertQuerysetEqual(Author.objects.all(), [])
  331. def test_delete_with_interpolated_redirect(self):
  332. a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'})
  333. res = self.client.post('/edit/author/%d/delete/interpolate_redirect/' % a.pk)
  334. self.assertEqual(res.status_code, 302)
  335. self.assertRedirects(res, '/edit/authors/create/?deleted=%d' % a.pk)
  336. self.assertQuerysetEqual(Author.objects.all(), [])
  337. # Also test with escaped chars in URL
  338. a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'})
  339. res = self.client.post('/edit/author/{}/delete/interpolate_redirect_nonascii/'.format(a.pk))
  340. self.assertEqual(res.status_code, 302)
  341. self.assertRedirects(res, '/%C3%A9dit/authors/create/?deleted={}'.format(a.pk))
  342. def test_delete_with_special_properties(self):
  343. a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'})
  344. res = self.client.get('/edit/author/%d/delete/special/' % a.pk)
  345. self.assertEqual(res.status_code, 200)
  346. self.assertEqual(res.context['object'], Author.objects.get(pk=a.pk))
  347. self.assertEqual(res.context['thingy'], Author.objects.get(pk=a.pk))
  348. self.assertNotIn('author', res.context)
  349. self.assertTemplateUsed(res, 'generic_views/confirm_delete.html')
  350. res = self.client.post('/edit/author/%d/delete/special/' % a.pk)
  351. self.assertEqual(res.status_code, 302)
  352. self.assertRedirects(res, '/list/authors/')
  353. self.assertQuerysetEqual(Author.objects.all(), [])
  354. def test_delete_without_redirect(self):
  355. a = Author.objects.create(
  356. name='Randall Munroe',
  357. slug='randall-munroe',
  358. )
  359. # Should raise exception -- No redirect URL provided, and no
  360. # get_absolute_url provided
  361. with self.assertRaises(ImproperlyConfigured):
  362. self.client.post('/edit/author/%d/delete/naive/' % a.pk)