Reverted [7986].
[django.git] / tests / modeltests / model_forms / models.py
blobcc9efd0f94a7455f32d93162001a52921783703b
1 """
2 XX. Generating HTML forms from models
4 This is mostly just a reworking of the form_for_model/form_for_instance tests
5 to use ModelForm. As such, the text may not make sense in all cases, and the
6 examples are probably a poor fit for the ModelForm syntax. In other words,
7 most of these tests should be rewritten.
8 """
10 import os
11 import tempfile
13 from django.db import models
15 ARTICLE_STATUS = (
16 (1, 'Draft'),
17 (2, 'Pending'),
18 (3, 'Live'),
21 class Category(models.Model):
22 name = models.CharField(max_length=20)
23 slug = models.SlugField(max_length=20)
24 url = models.CharField('The URL', max_length=40)
26 def __unicode__(self):
27 return self.name
29 class Writer(models.Model):
30 name = models.CharField(max_length=50, help_text='Use both first and last names.')
32 def __unicode__(self):
33 return self.name
35 class Article(models.Model):
36 headline = models.CharField(max_length=50)
37 slug = models.SlugField()
38 pub_date = models.DateField()
39 created = models.DateField(editable=False)
40 writer = models.ForeignKey(Writer)
41 article = models.TextField()
42 categories = models.ManyToManyField(Category, blank=True)
43 status = models.IntegerField(choices=ARTICLE_STATUS, blank=True, null=True)
45 def save(self):
46 import datetime
47 if not self.id:
48 self.created = datetime.date.today()
49 return super(Article, self).save()
51 def __unicode__(self):
52 return self.headline
54 class PhoneNumber(models.Model):
55 phone = models.PhoneNumberField()
56 description = models.CharField(max_length=20)
58 def __unicode__(self):
59 return self.phone
61 class TextFile(models.Model):
62 description = models.CharField(max_length=20)
63 file = models.FileField(upload_to=tempfile.gettempdir())
65 def __unicode__(self):
66 return self.description
68 class ImageFile(models.Model):
69 description = models.CharField(max_length=20)
70 try:
71 # If PIL is available, try testing PIL.
72 # Otherwise, it's equivalent to TextFile above.
73 import Image
74 image = models.ImageField(upload_to=tempfile.gettempdir())
75 except ImportError:
76 image = models.FileField(upload_to=tempfile.gettempdir())
78 def __unicode__(self):
79 return self.description
81 __test__ = {'API_TESTS': """
82 >>> from django import forms
83 >>> from django.forms.models import ModelForm
84 >>> from django.core.files.uploadedfile import SimpleUploadedFile
86 The bare bones, absolutely nothing custom, basic case.
88 >>> class CategoryForm(ModelForm):
89 ... class Meta:
90 ... model = Category
91 >>> CategoryForm.base_fields.keys()
92 ['name', 'slug', 'url']
95 Extra fields.
97 >>> class CategoryForm(ModelForm):
98 ... some_extra_field = forms.BooleanField()
99 ...
100 ... class Meta:
101 ... model = Category
103 >>> CategoryForm.base_fields.keys()
104 ['name', 'slug', 'url', 'some_extra_field']
107 Replacing a field.
109 >>> class CategoryForm(ModelForm):
110 ... url = forms.BooleanField()
112 ... class Meta:
113 ... model = Category
115 >>> CategoryForm.base_fields['url'].__class__
116 <class 'django.forms.fields.BooleanField'>
119 Using 'fields'.
121 >>> class CategoryForm(ModelForm):
123 ... class Meta:
124 ... model = Category
125 ... fields = ['url']
127 >>> CategoryForm.base_fields.keys()
128 ['url']
131 Using 'exclude'
133 >>> class CategoryForm(ModelForm):
135 ... class Meta:
136 ... model = Category
137 ... exclude = ['url']
139 >>> CategoryForm.base_fields.keys()
140 ['name', 'slug']
143 Using 'fields' *and* 'exclude'. Not sure why you'd want to do this, but uh,
144 "be liberal in what you accept" and all.
146 >>> class CategoryForm(ModelForm):
148 ... class Meta:
149 ... model = Category
150 ... fields = ['name', 'url']
151 ... exclude = ['url']
153 >>> CategoryForm.base_fields.keys()
154 ['name']
156 Don't allow more than one 'model' definition in the inheritance hierarchy.
157 Technically, it would generate a valid form, but the fact that the resulting
158 save method won't deal with multiple objects is likely to trip up people not
159 familiar with the mechanics.
161 >>> class CategoryForm(ModelForm):
162 ... class Meta:
163 ... model = Category
165 >>> class OddForm(CategoryForm):
166 ... class Meta:
167 ... model = Article
169 OddForm is now an Article-related thing, because BadForm.Meta overrides
170 CategoryForm.Meta.
171 >>> OddForm.base_fields.keys()
172 ['headline', 'slug', 'pub_date', 'writer', 'article', 'status', 'categories']
174 >>> class ArticleForm(ModelForm):
175 ... class Meta:
176 ... model = Article
178 First class with a Meta class wins.
180 >>> class BadForm(ArticleForm, CategoryForm):
181 ... pass
182 >>> OddForm.base_fields.keys()
183 ['headline', 'slug', 'pub_date', 'writer', 'article', 'status', 'categories']
185 Subclassing without specifying a Meta on the class will use the parent's Meta
186 (or the first parent in the MRO if there are multiple parent classes).
188 >>> class CategoryForm(ModelForm):
189 ... class Meta:
190 ... model = Category
191 >>> class SubCategoryForm(CategoryForm):
192 ... pass
193 >>> SubCategoryForm.base_fields.keys()
194 ['name', 'slug', 'url']
196 We can also subclass the Meta inner class to change the fields list.
198 >>> class CategoryForm(ModelForm):
199 ... checkbox = forms.BooleanField()
201 ... class Meta:
202 ... model = Category
203 >>> class SubCategoryForm(CategoryForm):
204 ... class Meta(CategoryForm.Meta):
205 ... exclude = ['url']
207 >>> print SubCategoryForm()
208 <tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr>
209 <tr><th><label for="id_slug">Slug:</label></th><td><input id="id_slug" type="text" name="slug" maxlength="20" /></td></tr>
210 <tr><th><label for="id_checkbox">Checkbox:</label></th><td><input type="checkbox" name="checkbox" id="id_checkbox" /></td></tr>
212 # Old form_for_x tests #######################################################
214 >>> from django.forms import ModelForm, CharField
215 >>> import datetime
217 >>> Category.objects.all()
220 >>> class CategoryForm(ModelForm):
221 ... class Meta:
222 ... model = Category
223 >>> f = CategoryForm()
224 >>> print f
225 <tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr>
226 <tr><th><label for="id_slug">Slug:</label></th><td><input id="id_slug" type="text" name="slug" maxlength="20" /></td></tr>
227 <tr><th><label for="id_url">The URL:</label></th><td><input id="id_url" type="text" name="url" maxlength="40" /></td></tr>
228 >>> print f.as_ul()
229 <li><label for="id_name">Name:</label> <input id="id_name" type="text" name="name" maxlength="20" /></li>
230 <li><label for="id_slug">Slug:</label> <input id="id_slug" type="text" name="slug" maxlength="20" /></li>
231 <li><label for="id_url">The URL:</label> <input id="id_url" type="text" name="url" maxlength="40" /></li>
232 >>> print f['name']
233 <input id="id_name" type="text" name="name" maxlength="20" />
235 >>> f = CategoryForm(auto_id=False)
236 >>> print f.as_ul()
237 <li>Name: <input type="text" name="name" maxlength="20" /></li>
238 <li>Slug: <input type="text" name="slug" maxlength="20" /></li>
239 <li>The URL: <input type="text" name="url" maxlength="40" /></li>
241 >>> f = CategoryForm({'name': 'Entertainment', 'slug': 'entertainment', 'url': 'entertainment'})
242 >>> f.is_valid()
243 True
244 >>> f.cleaned_data['url']
245 u'entertainment'
246 >>> f.cleaned_data['name']
247 u'Entertainment'
248 >>> f.cleaned_data['slug']
249 u'entertainment'
250 >>> obj = f.save()
251 >>> obj
252 <Category: Entertainment>
253 >>> Category.objects.all()
254 [<Category: Entertainment>]
256 >>> f = CategoryForm({'name': "It's a test", 'slug': 'its-test', 'url': 'test'})
257 >>> f.is_valid()
258 True
259 >>> f.cleaned_data['url']
260 u'test'
261 >>> f.cleaned_data['name']
262 u"It's a test"
263 >>> f.cleaned_data['slug']
264 u'its-test'
265 >>> obj = f.save()
266 >>> obj
267 <Category: It's a test>
268 >>> Category.objects.order_by('name')
269 [<Category: Entertainment>, <Category: It's a test>]
271 If you call save() with commit=False, then it will return an object that
272 hasn't yet been saved to the database. In this case, it's up to you to call
273 save() on the resulting model instance.
274 >>> f = CategoryForm({'name': 'Third test', 'slug': 'third-test', 'url': 'third'})
275 >>> f.is_valid()
276 True
277 >>> f.cleaned_data['url']
278 u'third'
279 >>> f.cleaned_data['name']
280 u'Third test'
281 >>> f.cleaned_data['slug']
282 u'third-test'
283 >>> obj = f.save(commit=False)
284 >>> obj
285 <Category: Third test>
286 >>> Category.objects.order_by('name')
287 [<Category: Entertainment>, <Category: It's a test>]
288 >>> obj.save()
289 >>> Category.objects.order_by('name')
290 [<Category: Entertainment>, <Category: It's a test>, <Category: Third test>]
292 If you call save() with invalid data, you'll get a ValueError.
293 >>> f = CategoryForm({'name': '', 'slug': '', 'url': 'foo'})
294 >>> f.errors['name']
295 [u'This field is required.']
296 >>> f.errors['slug']
297 [u'This field is required.']
298 >>> f.cleaned_data
299 Traceback (most recent call last):
301 AttributeError: 'CategoryForm' object has no attribute 'cleaned_data'
302 >>> f.save()
303 Traceback (most recent call last):
305 ValueError: The Category could not be created because the data didn't validate.
306 >>> f = CategoryForm({'name': '', 'slug': '', 'url': 'foo'})
307 >>> f.save()
308 Traceback (most recent call last):
310 ValueError: The Category could not be created because the data didn't validate.
312 Create a couple of Writers.
313 >>> w = Writer(name='Mike Royko')
314 >>> w.save()
315 >>> w = Writer(name='Bob Woodward')
316 >>> w.save()
318 ManyToManyFields are represented by a MultipleChoiceField, ForeignKeys and any
319 fields with the 'choices' attribute are represented by a ChoiceField.
320 >>> class ArticleForm(ModelForm):
321 ... class Meta:
322 ... model = Article
323 >>> f = ArticleForm(auto_id=False)
324 >>> print f
325 <tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
326 <tr><th>Slug:</th><td><input type="text" name="slug" maxlength="50" /></td></tr>
327 <tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr>
328 <tr><th>Writer:</th><td><select name="writer">
329 <option value="" selected="selected">---------</option>
330 <option value="1">Mike Royko</option>
331 <option value="2">Bob Woodward</option>
332 </select></td></tr>
333 <tr><th>Article:</th><td><textarea rows="10" cols="40" name="article"></textarea></td></tr>
334 <tr><th>Status:</th><td><select name="status">
335 <option value="" selected="selected">---------</option>
336 <option value="1">Draft</option>
337 <option value="2">Pending</option>
338 <option value="3">Live</option>
339 </select></td></tr>
340 <tr><th>Categories:</th><td><select multiple="multiple" name="categories">
341 <option value="1">Entertainment</option>
342 <option value="2">It&#39;s a test</option>
343 <option value="3">Third test</option>
344 </select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr>
346 You can restrict a form to a subset of the complete list of fields
347 by providing a 'fields' argument. If you try to save a
348 model created with such a form, you need to ensure that the fields
349 that are _not_ on the form have default values, or are allowed to have
350 a value of None. If a field isn't specified on a form, the object created
351 from the form can't provide a value for that field!
352 >>> class PartialArticleForm(ModelForm):
353 ... class Meta:
354 ... model = Article
355 ... fields = ('headline','pub_date')
356 >>> f = PartialArticleForm(auto_id=False)
357 >>> print f
358 <tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
359 <tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr>
361 Use form_for_instance to create a Form from a model instance. The difference
362 between this Form and one created via form_for_model is that the object's
363 current values are inserted as 'initial' data in each Field.
364 >>> w = Writer.objects.get(name='Mike Royko')
365 >>> class RoykoForm(ModelForm):
366 ... class Meta:
367 ... model = Writer
368 >>> f = RoykoForm(auto_id=False, instance=w)
369 >>> print f
370 <tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br />Use both first and last names.</td></tr>
372 >>> art = Article(headline='Test article', slug='test-article', pub_date=datetime.date(1988, 1, 4), writer=w, article='Hello.')
373 >>> art.save()
374 >>> art.id
376 >>> class TestArticleForm(ModelForm):
377 ... class Meta:
378 ... model = Article
379 >>> f = TestArticleForm(auto_id=False, instance=art)
380 >>> print f.as_ul()
381 <li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" /></li>
382 <li>Slug: <input type="text" name="slug" value="test-article" maxlength="50" /></li>
383 <li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li>
384 <li>Writer: <select name="writer">
385 <option value="">---------</option>
386 <option value="1" selected="selected">Mike Royko</option>
387 <option value="2">Bob Woodward</option>
388 </select></li>
389 <li>Article: <textarea rows="10" cols="40" name="article">Hello.</textarea></li>
390 <li>Status: <select name="status">
391 <option value="" selected="selected">---------</option>
392 <option value="1">Draft</option>
393 <option value="2">Pending</option>
394 <option value="3">Live</option>
395 </select></li>
396 <li>Categories: <select multiple="multiple" name="categories">
397 <option value="1">Entertainment</option>
398 <option value="2">It&#39;s a test</option>
399 <option value="3">Third test</option>
400 </select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
401 >>> f = TestArticleForm({'headline': u'Test headline', 'slug': 'test-headline', 'pub_date': u'1984-02-06', 'writer': u'1', 'article': 'Hello.'}, instance=art)
402 >>> f.is_valid()
403 True
404 >>> test_art = f.save()
405 >>> test_art.id
407 >>> test_art = Article.objects.get(id=1)
408 >>> test_art.headline
409 u'Test headline'
411 You can create a form over a subset of the available fields
412 by specifying a 'fields' argument to form_for_instance.
413 >>> class PartialArticleForm(ModelForm):
414 ... class Meta:
415 ... model = Article
416 ... fields=('headline', 'slug', 'pub_date')
417 >>> f = PartialArticleForm({'headline': u'New headline', 'slug': 'new-headline', 'pub_date': u'1988-01-04'}, auto_id=False, instance=art)
418 >>> print f.as_ul()
419 <li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
420 <li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li>
421 <li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li>
422 >>> f.is_valid()
423 True
424 >>> new_art = f.save()
425 >>> new_art.id
427 >>> new_art = Article.objects.get(id=1)
428 >>> new_art.headline
429 u'New headline'
431 Add some categories and test the many-to-many form output.
432 >>> new_art.categories.all()
434 >>> new_art.categories.add(Category.objects.get(name='Entertainment'))
435 >>> new_art.categories.all()
436 [<Category: Entertainment>]
437 >>> class TestArticleForm(ModelForm):
438 ... class Meta:
439 ... model = Article
440 >>> f = TestArticleForm(auto_id=False, instance=new_art)
441 >>> print f.as_ul()
442 <li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
443 <li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li>
444 <li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li>
445 <li>Writer: <select name="writer">
446 <option value="">---------</option>
447 <option value="1" selected="selected">Mike Royko</option>
448 <option value="2">Bob Woodward</option>
449 </select></li>
450 <li>Article: <textarea rows="10" cols="40" name="article">Hello.</textarea></li>
451 <li>Status: <select name="status">
452 <option value="" selected="selected">---------</option>
453 <option value="1">Draft</option>
454 <option value="2">Pending</option>
455 <option value="3">Live</option>
456 </select></li>
457 <li>Categories: <select multiple="multiple" name="categories">
458 <option value="1" selected="selected">Entertainment</option>
459 <option value="2">It&#39;s a test</option>
460 <option value="3">Third test</option>
461 </select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
463 >>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
464 ... 'writer': u'1', 'article': u'Hello.', 'categories': [u'1', u'2']}, instance=new_art)
465 >>> new_art = f.save()
466 >>> new_art.id
468 >>> new_art = Article.objects.get(id=1)
469 >>> new_art.categories.order_by('name')
470 [<Category: Entertainment>, <Category: It's a test>]
472 Now, submit form data with no categories. This deletes the existing categories.
473 >>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
474 ... 'writer': u'1', 'article': u'Hello.'}, instance=new_art)
475 >>> new_art = f.save()
476 >>> new_art.id
478 >>> new_art = Article.objects.get(id=1)
479 >>> new_art.categories.all()
482 Create a new article, with categories, via the form.
483 >>> class ArticleForm(ModelForm):
484 ... class Meta:
485 ... model = Article
486 >>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
487 ... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
488 >>> new_art = f.save()
489 >>> new_art.id
491 >>> new_art = Article.objects.get(id=2)
492 >>> new_art.categories.order_by('name')
493 [<Category: Entertainment>, <Category: It's a test>]
495 Create a new article, with no categories, via the form.
496 >>> class ArticleForm(ModelForm):
497 ... class Meta:
498 ... model = Article
499 >>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
500 ... 'writer': u'1', 'article': u'Test.'})
501 >>> new_art = f.save()
502 >>> new_art.id
504 >>> new_art = Article.objects.get(id=3)
505 >>> new_art.categories.all()
508 Create a new article, with categories, via the form, but use commit=False.
509 The m2m data won't be saved until save_m2m() is invoked on the form.
510 >>> class ArticleForm(ModelForm):
511 ... class Meta:
512 ... model = Article
513 >>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': 'walrus-was-paul', 'pub_date': u'1967-11-01',
514 ... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
515 >>> new_art = f.save(commit=False)
517 # Manually save the instance
518 >>> new_art.save()
519 >>> new_art.id
522 # The instance doesn't have m2m data yet
523 >>> new_art = Article.objects.get(id=4)
524 >>> new_art.categories.all()
527 # Save the m2m data on the form
528 >>> f.save_m2m()
529 >>> new_art.categories.order_by('name')
530 [<Category: Entertainment>, <Category: It's a test>]
532 Here, we define a custom ModelForm. Because it happens to have the same fields as
533 the Category model, we can just call the form's save() to apply its changes to an
534 existing Category instance.
535 >>> class ShortCategory(ModelForm):
536 ... name = CharField(max_length=5)
537 ... slug = CharField(max_length=5)
538 ... url = CharField(max_length=3)
539 >>> cat = Category.objects.get(name='Third test')
540 >>> cat
541 <Category: Third test>
542 >>> cat.id
544 >>> form = ShortCategory({'name': 'Third', 'slug': 'third', 'url': '3rd'}, instance=cat)
545 >>> form.save()
546 <Category: Third>
547 >>> Category.objects.get(id=3)
548 <Category: Third>
550 Here, we demonstrate that choices for a ForeignKey ChoiceField are determined
551 at runtime, based on the data in the database when the form is displayed, not
552 the data in the database when the form is instantiated.
553 >>> class ArticleForm(ModelForm):
554 ... class Meta:
555 ... model = Article
556 >>> f = ArticleForm(auto_id=False)
557 >>> print f.as_ul()
558 <li>Headline: <input type="text" name="headline" maxlength="50" /></li>
559 <li>Slug: <input type="text" name="slug" maxlength="50" /></li>
560 <li>Pub date: <input type="text" name="pub_date" /></li>
561 <li>Writer: <select name="writer">
562 <option value="" selected="selected">---------</option>
563 <option value="1">Mike Royko</option>
564 <option value="2">Bob Woodward</option>
565 </select></li>
566 <li>Article: <textarea rows="10" cols="40" name="article"></textarea></li>
567 <li>Status: <select name="status">
568 <option value="" selected="selected">---------</option>
569 <option value="1">Draft</option>
570 <option value="2">Pending</option>
571 <option value="3">Live</option>
572 </select></li>
573 <li>Categories: <select multiple="multiple" name="categories">
574 <option value="1">Entertainment</option>
575 <option value="2">It&#39;s a test</option>
576 <option value="3">Third</option>
577 </select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
578 >>> Category.objects.create(name='Fourth', url='4th')
579 <Category: Fourth>
580 >>> Writer.objects.create(name='Carl Bernstein')
581 <Writer: Carl Bernstein>
582 >>> print f.as_ul()
583 <li>Headline: <input type="text" name="headline" maxlength="50" /></li>
584 <li>Slug: <input type="text" name="slug" maxlength="50" /></li>
585 <li>Pub date: <input type="text" name="pub_date" /></li>
586 <li>Writer: <select name="writer">
587 <option value="" selected="selected">---------</option>
588 <option value="1">Mike Royko</option>
589 <option value="2">Bob Woodward</option>
590 <option value="3">Carl Bernstein</option>
591 </select></li>
592 <li>Article: <textarea rows="10" cols="40" name="article"></textarea></li>
593 <li>Status: <select name="status">
594 <option value="" selected="selected">---------</option>
595 <option value="1">Draft</option>
596 <option value="2">Pending</option>
597 <option value="3">Live</option>
598 </select></li>
599 <li>Categories: <select multiple="multiple" name="categories">
600 <option value="1">Entertainment</option>
601 <option value="2">It&#39;s a test</option>
602 <option value="3">Third</option>
603 <option value="4">Fourth</option>
604 </select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
606 # ModelChoiceField ############################################################
608 >>> from django.forms import ModelChoiceField, ModelMultipleChoiceField
610 >>> f = ModelChoiceField(Category.objects.all())
611 >>> list(f.choices)
612 [(u'', u'---------'), (1, u'Entertainment'), (2, u"It's a test"), (3, u'Third'), (4, u'Fourth')]
613 >>> f.clean('')
614 Traceback (most recent call last):
616 ValidationError: [u'This field is required.']
617 >>> f.clean(None)
618 Traceback (most recent call last):
620 ValidationError: [u'This field is required.']
621 >>> f.clean(0)
622 Traceback (most recent call last):
624 ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
625 >>> f.clean(3)
626 <Category: Third>
627 >>> f.clean(2)
628 <Category: It's a test>
630 # Add a Category object *after* the ModelChoiceField has already been
631 # instantiated. This proves clean() checks the database during clean() rather
632 # than caching it at time of instantiation.
633 >>> Category.objects.create(name='Fifth', url='5th')
634 <Category: Fifth>
635 >>> f.clean(5)
636 <Category: Fifth>
638 # Delete a Category object *after* the ModelChoiceField has already been
639 # instantiated. This proves clean() checks the database during clean() rather
640 # than caching it at time of instantiation.
641 >>> Category.objects.get(url='5th').delete()
642 >>> f.clean(5)
643 Traceback (most recent call last):
645 ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
647 >>> f = ModelChoiceField(Category.objects.filter(pk=1), required=False)
648 >>> print f.clean('')
649 None
650 >>> f.clean('')
651 >>> f.clean('1')
652 <Category: Entertainment>
653 >>> f.clean('100')
654 Traceback (most recent call last):
656 ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
658 # queryset can be changed after the field is created.
659 >>> f.queryset = Category.objects.exclude(name='Fourth')
660 >>> list(f.choices)
661 [(u'', u'---------'), (1, u'Entertainment'), (2, u"It's a test"), (3, u'Third')]
662 >>> f.clean(3)
663 <Category: Third>
664 >>> f.clean(4)
665 Traceback (most recent call last):
667 ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
669 # check that we can safely iterate choices repeatedly
670 >>> gen_one = list(f.choices)
671 >>> gen_two = f.choices
672 >>> gen_one[2]
673 (2L, u"It's a test")
674 >>> list(gen_two)
675 [(u'', u'---------'), (1L, u'Entertainment'), (2L, u"It's a test"), (3L, u'Third')]
677 # check that we can override the label_from_instance method to print custom labels (#4620)
678 >>> f.queryset = Category.objects.all()
679 >>> f.label_from_instance = lambda obj: "category " + str(obj)
680 >>> list(f.choices)
681 [(u'', u'---------'), (1L, 'category Entertainment'), (2L, "category It's a test"), (3L, 'category Third'), (4L, 'category Fourth')]
683 # ModelMultipleChoiceField ####################################################
685 >>> f = ModelMultipleChoiceField(Category.objects.all())
686 >>> list(f.choices)
687 [(1, u'Entertainment'), (2, u"It's a test"), (3, u'Third'), (4, u'Fourth')]
688 >>> f.clean(None)
689 Traceback (most recent call last):
691 ValidationError: [u'This field is required.']
692 >>> f.clean([])
693 Traceback (most recent call last):
695 ValidationError: [u'This field is required.']
696 >>> f.clean([1])
697 [<Category: Entertainment>]
698 >>> f.clean([2])
699 [<Category: It's a test>]
700 >>> f.clean(['1'])
701 [<Category: Entertainment>]
702 >>> f.clean(['1', '2'])
703 [<Category: Entertainment>, <Category: It's a test>]
704 >>> f.clean([1, '2'])
705 [<Category: Entertainment>, <Category: It's a test>]
706 >>> f.clean((1, '2'))
707 [<Category: Entertainment>, <Category: It's a test>]
708 >>> f.clean(['100'])
709 Traceback (most recent call last):
711 ValidationError: [u'Select a valid choice. 100 is not one of the available choices.']
712 >>> f.clean('hello')
713 Traceback (most recent call last):
715 ValidationError: [u'Enter a list of values.']
717 # Add a Category object *after* the ModelMultipleChoiceField has already been
718 # instantiated. This proves clean() checks the database during clean() rather
719 # than caching it at time of instantiation.
720 >>> Category.objects.create(id=6, name='Sixth', url='6th')
721 <Category: Sixth>
722 >>> f.clean([6])
723 [<Category: Sixth>]
725 # Delete a Category object *after* the ModelMultipleChoiceField has already been
726 # instantiated. This proves clean() checks the database during clean() rather
727 # than caching it at time of instantiation.
728 >>> Category.objects.get(url='6th').delete()
729 >>> f.clean([6])
730 Traceback (most recent call last):
732 ValidationError: [u'Select a valid choice. 6 is not one of the available choices.']
734 >>> f = ModelMultipleChoiceField(Category.objects.all(), required=False)
735 >>> f.clean([])
737 >>> f.clean(())
739 >>> f.clean(['10'])
740 Traceback (most recent call last):
742 ValidationError: [u'Select a valid choice. 10 is not one of the available choices.']
743 >>> f.clean(['3', '10'])
744 Traceback (most recent call last):
746 ValidationError: [u'Select a valid choice. 10 is not one of the available choices.']
747 >>> f.clean(['1', '10'])
748 Traceback (most recent call last):
750 ValidationError: [u'Select a valid choice. 10 is not one of the available choices.']
752 # queryset can be changed after the field is created.
753 >>> f.queryset = Category.objects.exclude(name='Fourth')
754 >>> list(f.choices)
755 [(1, u'Entertainment'), (2, u"It's a test"), (3, u'Third')]
756 >>> f.clean([3])
757 [<Category: Third>]
758 >>> f.clean([4])
759 Traceback (most recent call last):
761 ValidationError: [u'Select a valid choice. 4 is not one of the available choices.']
762 >>> f.clean(['3', '4'])
763 Traceback (most recent call last):
765 ValidationError: [u'Select a valid choice. 4 is not one of the available choices.']
767 >>> f.queryset = Category.objects.all()
768 >>> f.label_from_instance = lambda obj: "multicategory " + str(obj)
769 >>> list(f.choices)
770 [(1L, 'multicategory Entertainment'), (2L, "multicategory It's a test"), (3L, 'multicategory Third'), (4L, 'multicategory Fourth')]
772 # PhoneNumberField ############################################################
774 >>> class PhoneNumberForm(ModelForm):
775 ... class Meta:
776 ... model = PhoneNumber
777 >>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'})
778 >>> f.is_valid()
779 True
780 >>> f.cleaned_data['phone']
781 u'312-555-1212'
782 >>> f.cleaned_data['description']
783 u'Assistance'
785 # FileField ###################################################################
787 >>> class TextFileForm(ModelForm):
788 ... class Meta:
789 ... model = TextFile
791 # Test conditions when files is either not given or empty.
793 >>> f = TextFileForm(data={'description': u'Assistance'})
794 >>> f.is_valid()
795 False
796 >>> f = TextFileForm(data={'description': u'Assistance'}, files={})
797 >>> f.is_valid()
798 False
800 # Upload a file and ensure it all works as expected.
802 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test1.txt', 'hello world')})
803 >>> f.is_valid()
804 True
805 >>> type(f.cleaned_data['file'])
806 <class 'django.core.files.uploadedfile.SimpleUploadedFile'>
807 >>> instance = f.save()
808 >>> instance.file
809 u'...test1.txt'
811 >>> os.unlink(instance.get_file_filename())
813 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test1.txt', 'hello world')})
814 >>> f.is_valid()
815 True
816 >>> type(f.cleaned_data['file'])
817 <class 'django.core.files.uploadedfile.SimpleUploadedFile'>
818 >>> instance = f.save()
819 >>> instance.file
820 u'...test1.txt'
822 # Edit an instance that already has the file defined in the model. This will not
823 # save the file again, but leave it exactly as it is.
825 >>> f = TextFileForm(data={'description': u'Assistance'}, instance=instance)
826 >>> f.is_valid()
827 True
828 >>> f.cleaned_data['file']
829 u'...test1.txt'
830 >>> instance = f.save()
831 >>> instance.file
832 u'...test1.txt'
834 # Delete the current file since this is not done by Django.
835 >>> os.unlink(instance.get_file_filename())
837 # Override the file by uploading a new one.
839 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test2.txt', 'hello world')}, instance=instance)
840 >>> f.is_valid()
841 True
842 >>> instance = f.save()
843 >>> instance.file
844 u'...test2.txt'
846 # Delete the current file since this is not done by Django.
847 >>> os.unlink(instance.get_file_filename())
849 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test2.txt', 'hello world')})
850 >>> f.is_valid()
851 True
852 >>> instance = f.save()
853 >>> instance.file
854 u'...test2.txt'
856 # Delete the current file since this is not done by Django.
857 >>> os.unlink(instance.get_file_filename())
859 >>> instance.delete()
861 # Test the non-required FileField
863 >>> f = TextFileForm(data={'description': u'Assistance'})
864 >>> f.fields['file'].required = False
865 >>> f.is_valid()
866 True
867 >>> instance = f.save()
868 >>> instance.file
871 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test3.txt', 'hello world')}, instance=instance)
872 >>> f.is_valid()
873 True
874 >>> instance = f.save()
875 >>> instance.file
876 u'...test3.txt'
878 # Delete the current file since this is not done by Django.
879 >>> os.unlink(instance.get_file_filename())
880 >>> instance.delete()
882 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test3.txt', 'hello world')})
883 >>> f.is_valid()
884 True
885 >>> instance = f.save()
886 >>> instance.file
887 u'...test3.txt'
889 # Delete the current file since this is not done by Django.
890 >>> os.unlink(instance.get_file_filename())
891 >>> instance.delete()
893 # ImageField ###################################################################
895 # ImageField and FileField are nearly identical, but they differ slighty when
896 # it comes to validation. This specifically tests that #6302 is fixed for
897 # both file fields and image fields.
899 >>> class ImageFileForm(ModelForm):
900 ... class Meta:
901 ... model = ImageFile
903 >>> image_data = open(os.path.join(os.path.dirname(__file__), "test.png"), 'rb').read()
905 >>> f = ImageFileForm(data={'description': u'An image'}, files={'image': SimpleUploadedFile('test.png', image_data)})
906 >>> f.is_valid()
907 True
908 >>> type(f.cleaned_data['image'])
909 <class 'django.core.files.uploadedfile.SimpleUploadedFile'>
910 >>> instance = f.save()
911 >>> instance.image
912 u'...test.png'
914 # Delete the current file since this is not done by Django.
915 >>> os.unlink(instance.get_image_filename())
917 >>> f = ImageFileForm(data={'description': u'An image'}, files={'image': SimpleUploadedFile('test.png', image_data)})
918 >>> f.is_valid()
919 True
920 >>> type(f.cleaned_data['image'])
921 <class 'django.core.files.uploadedfile.SimpleUploadedFile'>
922 >>> instance = f.save()
923 >>> instance.image
924 u'...test.png'
926 # Edit an instance that already has the image defined in the model. This will not
927 # save the image again, but leave it exactly as it is.
929 >>> f = ImageFileForm(data={'description': u'Look, it changed'}, instance=instance)
930 >>> f.is_valid()
931 True
932 >>> f.cleaned_data['image']
933 u'...test.png'
934 >>> instance = f.save()
935 >>> instance.image
936 u'...test.png'
938 # Delete the current image since this is not done by Django.
940 >>> os.unlink(instance.get_image_filename())
942 # Override the file by uploading a new one.
944 >>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': SimpleUploadedFile('test2.png', image_data)}, instance=instance)
945 >>> f.is_valid()
946 True
947 >>> instance = f.save()
948 >>> instance.image
949 u'...test2.png'
951 # Delete the current file since this is not done by Django.
952 >>> os.unlink(instance.get_image_filename())
953 >>> instance.delete()
955 >>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': SimpleUploadedFile('test2.png', image_data)})
956 >>> f.is_valid()
957 True
958 >>> instance = f.save()
959 >>> instance.image
960 u'...test2.png'
962 # Delete the current file since this is not done by Django.
963 >>> os.unlink(instance.get_image_filename())
964 >>> instance.delete()
966 # Test the non-required ImageField
968 >>> f = ImageFileForm(data={'description': u'Test'})
969 >>> f.fields['image'].required = False
970 >>> f.is_valid()
971 True
972 >>> instance = f.save()
973 >>> instance.image
976 >>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': SimpleUploadedFile('test3.png', image_data)}, instance=instance)
977 >>> f.is_valid()
978 True
979 >>> instance = f.save()
980 >>> instance.image
981 u'...test3.png'
983 # Delete the current file since this is not done by Django.
984 >>> os.unlink(instance.get_image_filename())
985 >>> instance.delete()
987 >>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': SimpleUploadedFile('test3.png', image_data)})
988 >>> f.is_valid()
989 True
990 >>> instance = f.save()
991 >>> instance.image
992 u'...test3.png'
993 >>> instance.delete()
995 # Media on a ModelForm ########################################################
997 # Similar to a regular Form class you can define custom media to be used on
998 # the ModelForm.
1000 >>> class ModelFormWithMedia(ModelForm):
1001 ... class Media:
1002 ... js = ('/some/form/javascript',)
1003 ... css = {
1004 ... 'all': ('/some/form/css',)
1005 ... }
1006 ... class Meta:
1007 ... model = PhoneNumber
1008 >>> f = ModelFormWithMedia()
1009 >>> print f.media
1010 <link href="/some/form/css" type="text/css" media="all" rel="stylesheet" />
1011 <script type="text/javascript" src="/some/form/javascript"></script>
1013 """}