tests.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. from datetime import date
  2. from django import forms
  3. from django.core.exceptions import FieldError, ValidationError
  4. from django.core.files.uploadedfile import SimpleUploadedFile
  5. from django.forms.models import (modelform_factory, ModelChoiceField,
  6. fields_for_model, construct_instance)
  7. from django.utils import unittest
  8. from django.test import TestCase
  9. from models import Person, RealPerson, Triple, FilePathModel, Article, \
  10. Publication, CustomFF, Author, Author1, Homepage, Document, Edition
  11. class ModelMultipleChoiceFieldTests(TestCase):
  12. def test_model_multiple_choice_number_of_queries(self):
  13. """
  14. Test that ModelMultipleChoiceField does O(1) queries instead of
  15. O(n) (#10156).
  16. """
  17. persons = [Person.objects.create(name="Person %s" % i) for i in range(30)]
  18. f = forms.ModelMultipleChoiceField(queryset=Person.objects.all())
  19. self.assertNumQueries(1, f.clean, [p.pk for p in persons[1:11:2]])
  20. def test_model_multiple_choice_run_validators(self):
  21. """
  22. Test that ModelMultipleChoiceField run given validators (#14144).
  23. """
  24. for i in range(30):
  25. Person.objects.create(name="Person %s" % i)
  26. self._validator_run = False
  27. def my_validator(value):
  28. self._validator_run = True
  29. f = forms.ModelMultipleChoiceField(queryset=Person.objects.all(),
  30. validators=[my_validator])
  31. f.clean([p.pk for p in Person.objects.all()[8:9]])
  32. self.assertTrue(self._validator_run)
  33. class TripleForm(forms.ModelForm):
  34. class Meta:
  35. model = Triple
  36. class UniqueTogetherTests(TestCase):
  37. def test_multiple_field_unique_together(self):
  38. """
  39. When the same field is involved in multiple unique_together
  40. constraints, we need to make sure we don't remove the data for it
  41. before doing all the validation checking (not just failing after
  42. the first one).
  43. """
  44. Triple.objects.create(left=1, middle=2, right=3)
  45. form = TripleForm({'left': '1', 'middle': '2', 'right': '3'})
  46. self.assertFalse(form.is_valid())
  47. form = TripleForm({'left': '1', 'middle': '3', 'right': '1'})
  48. self.assertTrue(form.is_valid())
  49. class TripleFormWithCleanOverride(forms.ModelForm):
  50. class Meta:
  51. model = Triple
  52. def clean(self):
  53. if not self.cleaned_data['left'] == self.cleaned_data['right']:
  54. raise forms.ValidationError('Left and right should be equal')
  55. return self.cleaned_data
  56. class OverrideCleanTests(TestCase):
  57. def test_override_clean(self):
  58. """
  59. Regression for #12596: Calling super from ModelForm.clean() should be
  60. optional.
  61. """
  62. form = TripleFormWithCleanOverride({'left': 1, 'middle': 2, 'right': 1})
  63. self.assertTrue(form.is_valid())
  64. # form.instance.left will be None if the instance was not constructed
  65. # by form.full_clean().
  66. self.assertEqual(form.instance.left, 1)
  67. # Regression test for #12960.
  68. # Make sure the cleaned_data returned from ModelForm.clean() is applied to the
  69. # model instance.
  70. class PublicationForm(forms.ModelForm):
  71. def clean(self):
  72. self.cleaned_data['title'] = self.cleaned_data['title'].upper()
  73. return self.cleaned_data
  74. class Meta:
  75. model = Publication
  76. class ModelFormCleanTest(TestCase):
  77. def test_model_form_clean_applies_to_model(self):
  78. data = {'title': 'test', 'date_published': '2010-2-25'}
  79. form = PublicationForm(data)
  80. publication = form.save()
  81. self.assertEqual(publication.title, 'TEST')
  82. class FPForm(forms.ModelForm):
  83. class Meta:
  84. model = FilePathModel
  85. class FilePathFieldTests(TestCase):
  86. def test_file_path_field_blank(self):
  87. """
  88. Regression test for #8842: FilePathField(blank=True)
  89. """
  90. form = FPForm()
  91. names = [p[1] for p in form['path'].field.choices]
  92. names.sort()
  93. self.assertEqual(names, ['---------', '__init__.py', 'models.py', 'tests.py'])
  94. class ManyToManyCallableInitialTests(TestCase):
  95. def test_callable(self):
  96. "Regression for #10349: A callable can be provided as the initial value for an m2m field"
  97. # Set up a callable initial value
  98. def formfield_for_dbfield(db_field, **kwargs):
  99. if db_field.name == 'publications':
  100. kwargs['initial'] = lambda: Publication.objects.all().order_by('date_published')[:2]
  101. return db_field.formfield(**kwargs)
  102. # Set up some Publications to use as data
  103. book1 = Publication.objects.create(title="First Book", date_published=date(2007,1,1))
  104. book2 = Publication.objects.create(title="Second Book", date_published=date(2008,1,1))
  105. book3 = Publication.objects.create(title="Third Book", date_published=date(2009,1,1))
  106. # Create a ModelForm, instantiate it, and check that the output is as expected
  107. ModelForm = modelform_factory(Article, formfield_callback=formfield_for_dbfield)
  108. form = ModelForm()
  109. self.assertEqual(form.as_ul(), u"""<li><label for="id_headline">Headline:</label> <input id="id_headline" type="text" name="headline" maxlength="100" /></li>
  110. <li><label for="id_publications">Publications:</label> <select multiple="multiple" name="publications" id="id_publications">
  111. <option value="%d" selected="selected">First Book</option>
  112. <option value="%d" selected="selected">Second Book</option>
  113. <option value="%d">Third Book</option>
  114. </select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>"""
  115. % (book1.pk, book2.pk, book3.pk))
  116. class CFFForm(forms.ModelForm):
  117. class Meta:
  118. model = CustomFF
  119. class CustomFieldSaveTests(TestCase):
  120. def test_save(self):
  121. "Regression for #11149: save_form_data should be called only once"
  122. # It's enough that the form saves without error -- the custom save routine will
  123. # generate an AssertionError if it is called more than once during save.
  124. form = CFFForm(data = {'f': None})
  125. form.save()
  126. class ModelChoiceIteratorTests(TestCase):
  127. def test_len(self):
  128. class Form(forms.ModelForm):
  129. class Meta:
  130. model = Article
  131. fields = ["publications"]
  132. Publication.objects.create(title="Pravda",
  133. date_published=date(1991, 8, 22))
  134. f = Form()
  135. self.assertEqual(len(f.fields["publications"].choices), 1)
  136. class RealPersonForm(forms.ModelForm):
  137. class Meta:
  138. model = RealPerson
  139. class CustomModelFormSaveMethod(TestCase):
  140. def test_string_message(self):
  141. data = {'name': 'anonymous'}
  142. form = RealPersonForm(data)
  143. self.assertEqual(form.is_valid(), False)
  144. self.assertEqual(form.errors['__all__'], ['Please specify a real name.'])
  145. class ModelClassTests(TestCase):
  146. def test_no_model_class(self):
  147. class NoModelModelForm(forms.ModelForm):
  148. pass
  149. self.assertRaises(ValueError, NoModelModelForm)
  150. class OneToOneFieldTests(TestCase):
  151. def test_assignment_of_none(self):
  152. class AuthorForm(forms.ModelForm):
  153. class Meta:
  154. model = Author
  155. fields = ['publication', 'full_name']
  156. publication = Publication.objects.create(title="Pravda",
  157. date_published=date(1991, 8, 22))
  158. author = Author.objects.create(publication=publication, full_name='John Doe')
  159. form = AuthorForm({'publication':u'', 'full_name':'John Doe'}, instance=author)
  160. self.assertTrue(form.is_valid())
  161. self.assertEqual(form.cleaned_data['publication'], None)
  162. author = form.save()
  163. # author object returned from form still retains original publication object
  164. # that's why we need to retreive it from database again
  165. new_author = Author.objects.get(pk=author.pk)
  166. self.assertEqual(new_author.publication, None)
  167. def test_assignment_of_none_null_false(self):
  168. class AuthorForm(forms.ModelForm):
  169. class Meta:
  170. model = Author1
  171. fields = ['publication', 'full_name']
  172. publication = Publication.objects.create(title="Pravda",
  173. date_published=date(1991, 8, 22))
  174. author = Author1.objects.create(publication=publication, full_name='John Doe')
  175. form = AuthorForm({'publication':u'', 'full_name':'John Doe'}, instance=author)
  176. self.assertTrue(not form.is_valid())
  177. class ModelChoiceForm(forms.Form):
  178. person = ModelChoiceField(Person.objects.all())
  179. class TestTicket11183(TestCase):
  180. def test_11183(self):
  181. form1 = ModelChoiceForm()
  182. field1 = form1.fields['person']
  183. # To allow the widget to change the queryset of field1.widget.choices correctly,
  184. # without affecting other forms, the following must hold:
  185. self.assertTrue(field1 is not ModelChoiceForm.base_fields['person'])
  186. self.assertTrue(field1.widget.choices.field is field1)
  187. class HomepageForm(forms.ModelForm):
  188. class Meta:
  189. model = Homepage
  190. class URLFieldTests(TestCase):
  191. def test_url_on_modelform(self):
  192. "Check basic URL field validation on model forms"
  193. self.assertFalse(HomepageForm({'url': 'foo'}).is_valid())
  194. self.assertFalse(HomepageForm({'url': 'http://'}).is_valid())
  195. self.assertFalse(HomepageForm({'url': 'http://example'}).is_valid())
  196. self.assertFalse(HomepageForm({'url': 'http://example.'}).is_valid())
  197. self.assertFalse(HomepageForm({'url': 'http://com.'}).is_valid())
  198. self.assertTrue(HomepageForm({'url': 'http://localhost'}).is_valid())
  199. self.assertTrue(HomepageForm({'url': 'http://example.com'}).is_valid())
  200. self.assertTrue(HomepageForm({'url': 'http://www.example.com'}).is_valid())
  201. self.assertTrue(HomepageForm({'url': 'http://www.example.com:8000'}).is_valid())
  202. self.assertTrue(HomepageForm({'url': 'http://www.example.com/test'}).is_valid())
  203. self.assertTrue(HomepageForm({'url': 'http://www.example.com:8000/test'}).is_valid())
  204. self.assertTrue(HomepageForm({'url': 'http://example.com/foo/bar'}).is_valid())
  205. def test_http_prefixing(self):
  206. "If the http:// prefix is omitted on form input, the field adds it again. (Refs #13613)"
  207. form = HomepageForm({'url': 'example.com'})
  208. form.is_valid()
  209. # self.assertTrue(form.is_valid())
  210. # self.assertEqual(form.cleaned_data['url'], 'http://example.com/')
  211. form = HomepageForm({'url': 'example.com/test'})
  212. form.is_valid()
  213. # self.assertTrue(form.is_valid())
  214. # self.assertEqual(form.cleaned_data['url'], 'http://example.com/test')
  215. class FormFieldCallbackTests(TestCase):
  216. def test_baseform_with_widgets_in_meta(self):
  217. """Regression for #13095: Using base forms with widgets defined in Meta should not raise errors."""
  218. widget = forms.Textarea()
  219. class BaseForm(forms.ModelForm):
  220. class Meta:
  221. model = Person
  222. widgets = {'name': widget}
  223. Form = modelform_factory(Person, form=BaseForm)
  224. self.assertTrue(Form.base_fields['name'].widget is widget)
  225. def test_custom_callback(self):
  226. """Test that a custom formfield_callback is used if provided"""
  227. callback_args = []
  228. def callback(db_field, **kwargs):
  229. callback_args.append((db_field, kwargs))
  230. return db_field.formfield(**kwargs)
  231. widget = forms.Textarea()
  232. class BaseForm(forms.ModelForm):
  233. class Meta:
  234. model = Person
  235. widgets = {'name': widget}
  236. _ = modelform_factory(Person, form=BaseForm,
  237. formfield_callback=callback)
  238. id_field, name_field = Person._meta.fields
  239. self.assertEqual(callback_args,
  240. [(id_field, {}), (name_field, {'widget': widget})])
  241. def test_bad_callback(self):
  242. # A bad callback provided by user still gives an error
  243. self.assertRaises(TypeError, modelform_factory, Person,
  244. formfield_callback='not a function or callable')
  245. class InvalidFieldAndFactory(TestCase):
  246. """ Tests for #11905 """
  247. def test_extra_field_model_form(self):
  248. try:
  249. class ExtraPersonForm(forms.ModelForm):
  250. """ ModelForm with an extra field """
  251. age = forms.IntegerField()
  252. class Meta:
  253. model = Person
  254. fields = ('name', 'no-field')
  255. except FieldError, e:
  256. # Make sure the exception contains some reference to the
  257. # field responsible for the problem.
  258. self.assertTrue('no-field' in e.args[0])
  259. else:
  260. self.fail('Invalid "no-field" field not caught')
  261. def test_extra_declared_field_model_form(self):
  262. try:
  263. class ExtraPersonForm(forms.ModelForm):
  264. """ ModelForm with an extra field """
  265. age = forms.IntegerField()
  266. class Meta:
  267. model = Person
  268. fields = ('name', 'age')
  269. except FieldError:
  270. self.fail('Declarative field raised FieldError incorrectly')
  271. def test_extra_field_modelform_factory(self):
  272. self.assertRaises(FieldError, modelform_factory,
  273. Person, fields=['no-field', 'name'])
  274. class DocumentForm(forms.ModelForm):
  275. class Meta:
  276. model = Document
  277. class FileFieldTests(unittest.TestCase):
  278. def test_clean_false(self):
  279. """
  280. If the ``clean`` method on a non-required FileField receives False as
  281. the data (meaning clear the field value), it returns False, regardless
  282. of the value of ``initial``.
  283. """
  284. f = forms.FileField(required=False)
  285. self.assertEqual(f.clean(False), False)
  286. self.assertEqual(f.clean(False, 'initial'), False)
  287. def test_clean_false_required(self):
  288. """
  289. If the ``clean`` method on a required FileField receives False as the
  290. data, it has the same effect as None: initial is returned if non-empty,
  291. otherwise the validation catches the lack of a required value.
  292. """
  293. f = forms.FileField(required=True)
  294. self.assertEqual(f.clean(False, 'initial'), 'initial')
  295. self.assertRaises(ValidationError, f.clean, False)
  296. def test_full_clear(self):
  297. """
  298. Integration happy-path test that a model FileField can actually be set
  299. and cleared via a ModelForm.
  300. """
  301. form = DocumentForm()
  302. self.assertTrue('name="myfile"' in unicode(form))
  303. self.assertTrue('myfile-clear' not in unicode(form))
  304. form = DocumentForm(files={'myfile': SimpleUploadedFile('something.txt', 'content')})
  305. self.assertTrue(form.is_valid())
  306. doc = form.save(commit=False)
  307. self.assertEqual(doc.myfile.name, 'something.txt')
  308. form = DocumentForm(instance=doc)
  309. self.assertTrue('myfile-clear' in unicode(form))
  310. form = DocumentForm(instance=doc, data={'myfile-clear': 'true'})
  311. doc = form.save(commit=False)
  312. self.assertEqual(bool(doc.myfile), False)
  313. def test_clear_and_file_contradiction(self):
  314. """
  315. If the user submits a new file upload AND checks the clear checkbox,
  316. they get a validation error, and the bound redisplay of the form still
  317. includes the current file and the clear checkbox.
  318. """
  319. form = DocumentForm(files={'myfile': SimpleUploadedFile('something.txt', 'content')})
  320. self.assertTrue(form.is_valid())
  321. doc = form.save(commit=False)
  322. form = DocumentForm(instance=doc,
  323. files={'myfile': SimpleUploadedFile('something.txt', 'content')},
  324. data={'myfile-clear': 'true'})
  325. self.assertTrue(not form.is_valid())
  326. self.assertEqual(form.errors['myfile'],
  327. [u'Please either submit a file or check the clear checkbox, not both.'])
  328. rendered = unicode(form)
  329. self.assertTrue('something.txt' in rendered)
  330. self.assertTrue('myfile-clear' in rendered)
  331. class EditionForm(forms.ModelForm):
  332. author = forms.ModelChoiceField(queryset=Person.objects.all())
  333. publication = forms.ModelChoiceField(queryset=Publication.objects.all())
  334. edition = forms.IntegerField()
  335. isbn = forms.CharField(max_length=13)
  336. class Meta:
  337. model = Edition
  338. class UniqueErrorsTests(TestCase):
  339. def setUp(self):
  340. self.author1 = Person.objects.create(name=u'Author #1')
  341. self.author2 = Person.objects.create(name=u'Author #2')
  342. self.pub1 = Publication.objects.create(title='Pub #1', date_published=date(2000, 10, 31))
  343. self.pub2 = Publication.objects.create(title='Pub #2', date_published=date(2004, 1, 5))
  344. form = EditionForm(data={'author': self.author1.pk, 'publication': self.pub1.pk, 'edition': 1, 'isbn': '9783161484100'})
  345. form.save()
  346. def test_unique_error_message(self):
  347. form = EditionForm(data={'author': self.author1.pk, 'publication': self.pub2.pk, 'edition': 1, 'isbn': '9783161484100'})
  348. self.assertEqual(form.errors, {'isbn': [u'Edition with this Isbn already exists.']})
  349. def test_unique_together_error_message(self):
  350. form = EditionForm(data={'author': self.author1.pk, 'publication': self.pub1.pk, 'edition': 2, 'isbn': '9783161489999'})
  351. self.assertEqual(form.errors, {'__all__': [u'Edition with this Author and Publication already exists.']})
  352. form = EditionForm(data={'author': self.author2.pk, 'publication': self.pub1.pk, 'edition': 1, 'isbn': '9783161487777'})
  353. self.assertEqual(form.errors, {'__all__': [u'Edition with this Publication and Edition already exists.']})
  354. class EmptyFieldsTestCase(TestCase):
  355. "Tests for fields=() cases as reported in #14119"
  356. class EmptyPersonForm(forms.ModelForm):
  357. class Meta:
  358. model = Person
  359. fields = ()
  360. def test_empty_fields_to_fields_for_model(self):
  361. "An argument of fields=() to fields_for_model should return an empty dictionary"
  362. field_dict = fields_for_model(Person, fields=())
  363. self.assertEqual(len(field_dict), 0)
  364. def test_empty_fields_on_modelform(self):
  365. "No fields on a ModelForm should actually result in no fields"
  366. form = self.EmptyPersonForm()
  367. self.assertEqual(len(form.fields), 0)
  368. def test_empty_fields_to_construct_instance(self):
  369. "No fields should be set on a model instance if construct_instance receives fields=()"
  370. form = modelform_factory(Person)({'name': 'John Doe'})
  371. self.assertTrue(form.is_valid())
  372. instance = construct_instance(form, Person(), fields=())
  373. self.assertEqual(instance.name, '')