2
0

test_edit.py 19 KB

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