PageRenderTime 147ms CodeModel.GetById 60ms app.highlight 50ms RepoModel.GetById 31ms app.codeStats 0ms

/tests/regressiontests/model_forms_regress/tests.py

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