parts/django/tests/modeltests/model_forms/models.py
changeset 69 c6bca38c1cbf
equal deleted inserted replaced
68:5ff1fc726848 69:c6bca38c1cbf
       
     1 """
       
     2 XX. Generating HTML forms from models
       
     3 
       
     4 This is mostly just a reworking of the ``form_for_model``/``form_for_instance``
       
     5 tests to use ``ModelForm``. As such, the text may not make sense in all cases,
       
     6 and the examples are probably a poor fit for the ``ModelForm`` syntax. In other
       
     7 words, most of these tests should be rewritten.
       
     8 """
       
     9 
       
    10 import os
       
    11 import tempfile
       
    12 
       
    13 from django.db import models
       
    14 from django.core.files.storage import FileSystemStorage
       
    15 
       
    16 temp_storage_dir = tempfile.mkdtemp()
       
    17 temp_storage = FileSystemStorage(temp_storage_dir)
       
    18 
       
    19 ARTICLE_STATUS = (
       
    20     (1, 'Draft'),
       
    21     (2, 'Pending'),
       
    22     (3, 'Live'),
       
    23 )
       
    24 
       
    25 ARTICLE_STATUS_CHAR = (
       
    26     ('d', 'Draft'),
       
    27     ('p', 'Pending'),
       
    28     ('l', 'Live'),
       
    29 )
       
    30 
       
    31 class Category(models.Model):
       
    32     name = models.CharField(max_length=20)
       
    33     slug = models.SlugField(max_length=20)
       
    34     url = models.CharField('The URL', max_length=40)
       
    35 
       
    36     def __unicode__(self):
       
    37         return self.name
       
    38 
       
    39 class Writer(models.Model):
       
    40     name = models.CharField(max_length=50, help_text='Use both first and last names.')
       
    41 
       
    42     def __unicode__(self):
       
    43         return self.name
       
    44 
       
    45 class Article(models.Model):
       
    46     headline = models.CharField(max_length=50)
       
    47     slug = models.SlugField()
       
    48     pub_date = models.DateField()
       
    49     created = models.DateField(editable=False)
       
    50     writer = models.ForeignKey(Writer)
       
    51     article = models.TextField()
       
    52     categories = models.ManyToManyField(Category, blank=True)
       
    53     status = models.PositiveIntegerField(choices=ARTICLE_STATUS, blank=True, null=True)
       
    54 
       
    55     def save(self):
       
    56         import datetime
       
    57         if not self.id:
       
    58             self.created = datetime.date.today()
       
    59         return super(Article, self).save()
       
    60 
       
    61     def __unicode__(self):
       
    62         return self.headline
       
    63 
       
    64 class ImprovedArticle(models.Model):
       
    65     article = models.OneToOneField(Article)
       
    66 
       
    67 class ImprovedArticleWithParentLink(models.Model):
       
    68     article = models.OneToOneField(Article, parent_link=True)
       
    69 
       
    70 class BetterWriter(Writer):
       
    71     score = models.IntegerField()
       
    72 
       
    73 class WriterProfile(models.Model):
       
    74     writer = models.OneToOneField(Writer, primary_key=True)
       
    75     age = models.PositiveIntegerField()
       
    76 
       
    77     def __unicode__(self):
       
    78         return "%s is %s" % (self.writer, self.age)
       
    79 
       
    80 from django.contrib.localflavor.us.models import PhoneNumberField
       
    81 class PhoneNumber(models.Model):
       
    82     phone = PhoneNumberField()
       
    83     description = models.CharField(max_length=20)
       
    84 
       
    85     def __unicode__(self):
       
    86         return self.phone
       
    87 
       
    88 class TextFile(models.Model):
       
    89     description = models.CharField(max_length=20)
       
    90     file = models.FileField(storage=temp_storage, upload_to='tests', max_length=15)
       
    91 
       
    92     def __unicode__(self):
       
    93         return self.description
       
    94 
       
    95 try:
       
    96     # If PIL is available, try testing ImageFields. Checking for the existence
       
    97     # of Image is enough for CPython, but for PyPy, you need to check for the
       
    98     # underlying modules If PIL is not available, ImageField tests are omitted.
       
    99     # Try to import PIL in either of the two ways it can end up installed.
       
   100     try:
       
   101         from PIL import Image, _imaging
       
   102     except ImportError:
       
   103         import Image, _imaging
       
   104 
       
   105     test_images = True
       
   106 
       
   107     class ImageFile(models.Model):
       
   108         def custom_upload_path(self, filename):
       
   109             path = self.path or 'tests'
       
   110             return '%s/%s' % (path, filename)
       
   111 
       
   112         description = models.CharField(max_length=20)
       
   113 
       
   114         # Deliberately put the image field *after* the width/height fields to
       
   115         # trigger the bug in #10404 with width/height not getting assigned.
       
   116         width = models.IntegerField(editable=False)
       
   117         height = models.IntegerField(editable=False)
       
   118         image = models.ImageField(storage=temp_storage, upload_to=custom_upload_path,
       
   119                                   width_field='width', height_field='height')
       
   120         path = models.CharField(max_length=16, blank=True, default='')
       
   121 
       
   122         def __unicode__(self):
       
   123             return self.description
       
   124 
       
   125     class OptionalImageFile(models.Model):
       
   126         def custom_upload_path(self, filename):
       
   127             path = self.path or 'tests'
       
   128             return '%s/%s' % (path, filename)
       
   129 
       
   130         description = models.CharField(max_length=20)
       
   131         image = models.ImageField(storage=temp_storage, upload_to=custom_upload_path,
       
   132                                   width_field='width', height_field='height',
       
   133                                   blank=True, null=True)
       
   134         width = models.IntegerField(editable=False, null=True)
       
   135         height = models.IntegerField(editable=False, null=True)
       
   136         path = models.CharField(max_length=16, blank=True, default='')
       
   137 
       
   138         def __unicode__(self):
       
   139             return self.description
       
   140 except ImportError:
       
   141     test_images = False
       
   142 
       
   143 class CommaSeparatedInteger(models.Model):
       
   144     field = models.CommaSeparatedIntegerField(max_length=20)
       
   145 
       
   146     def __unicode__(self):
       
   147         return self.field
       
   148 
       
   149 class Product(models.Model):
       
   150     slug = models.SlugField(unique=True)
       
   151 
       
   152     def __unicode__(self):
       
   153         return self.slug
       
   154 
       
   155 class Price(models.Model):
       
   156     price = models.DecimalField(max_digits=10, decimal_places=2)
       
   157     quantity = models.PositiveIntegerField()
       
   158 
       
   159     def __unicode__(self):
       
   160         return u"%s for %s" % (self.quantity, self.price)
       
   161 
       
   162     class Meta:
       
   163         unique_together = (('price', 'quantity'),)
       
   164 
       
   165 class ArticleStatus(models.Model):
       
   166     status = models.CharField(max_length=2, choices=ARTICLE_STATUS_CHAR, blank=True, null=True)
       
   167 
       
   168 class Inventory(models.Model):
       
   169    barcode = models.PositiveIntegerField(unique=True)
       
   170    parent = models.ForeignKey('self', to_field='barcode', blank=True, null=True)
       
   171    name = models.CharField(blank=False, max_length=20)
       
   172 
       
   173    def __unicode__(self):
       
   174       return self.name
       
   175 
       
   176 class Book(models.Model):
       
   177     title = models.CharField(max_length=40)
       
   178     author = models.ForeignKey(Writer, blank=True, null=True)
       
   179     special_id = models.IntegerField(blank=True, null=True, unique=True)
       
   180 
       
   181     class Meta:
       
   182         unique_together = ('title', 'author')
       
   183 
       
   184 class BookXtra(models.Model):
       
   185     isbn = models.CharField(max_length=16, unique=True)
       
   186     suffix1 = models.IntegerField(blank=True, default=0)
       
   187     suffix2 = models.IntegerField(blank=True, default=0)
       
   188 
       
   189     class Meta:
       
   190         unique_together = (('suffix1', 'suffix2'))
       
   191         abstract = True
       
   192 
       
   193 class DerivedBook(Book, BookXtra):
       
   194     pass
       
   195 
       
   196 class ExplicitPK(models.Model):
       
   197     key = models.CharField(max_length=20, primary_key=True)
       
   198     desc = models.CharField(max_length=20, blank=True, unique=True)
       
   199     class Meta:
       
   200         unique_together = ('key', 'desc')
       
   201 
       
   202     def __unicode__(self):
       
   203         return self.key
       
   204 
       
   205 class Post(models.Model):
       
   206     title = models.CharField(max_length=50, unique_for_date='posted', blank=True)
       
   207     slug = models.CharField(max_length=50, unique_for_year='posted', blank=True)
       
   208     subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True)
       
   209     posted = models.DateField()
       
   210 
       
   211     def __unicode__(self):
       
   212         return self.name
       
   213 
       
   214 class DerivedPost(Post):
       
   215     pass
       
   216 
       
   217 class BigInt(models.Model):
       
   218     biggie = models.BigIntegerField()
       
   219 
       
   220     def __unicode__(self):
       
   221         return unicode(self.biggie)
       
   222 
       
   223 class MarkupField(models.CharField):
       
   224     def __init__(self, *args, **kwargs):
       
   225         kwargs["max_length"] = 20
       
   226         super(MarkupField, self).__init__(*args, **kwargs)
       
   227 
       
   228     def formfield(self, **kwargs):
       
   229         # don't allow this field to be used in form (real use-case might be
       
   230         # that you know the markup will always be X, but it is among an app
       
   231         # that allows the user to say it could be something else)
       
   232         # regressed at r10062
       
   233         return None
       
   234 
       
   235 class CustomFieldForExclusionModel(models.Model):
       
   236     name = models.CharField(max_length=10)
       
   237     markup = MarkupField()
       
   238 
       
   239 __test__ = {'API_TESTS': """
       
   240 >>> from django import forms
       
   241 >>> from django.forms.models import ModelForm, model_to_dict
       
   242 >>> from django.core.files.uploadedfile import SimpleUploadedFile
       
   243 
       
   244 The bare bones, absolutely nothing custom, basic case.
       
   245 
       
   246 >>> class CategoryForm(ModelForm):
       
   247 ...     class Meta:
       
   248 ...         model = Category
       
   249 >>> CategoryForm.base_fields.keys()
       
   250 ['name', 'slug', 'url']
       
   251 
       
   252 
       
   253 Extra fields.
       
   254 
       
   255 >>> class CategoryForm(ModelForm):
       
   256 ...     some_extra_field = forms.BooleanField()
       
   257 ...
       
   258 ...     class Meta:
       
   259 ...         model = Category
       
   260 
       
   261 >>> CategoryForm.base_fields.keys()
       
   262 ['name', 'slug', 'url', 'some_extra_field']
       
   263 
       
   264 Extra field that has a name collision with a related object accessor.
       
   265 
       
   266 >>> class WriterForm(ModelForm):
       
   267 ...     book = forms.CharField(required=False)
       
   268 ...
       
   269 ...     class Meta:
       
   270 ...         model = Writer
       
   271 
       
   272 >>> wf = WriterForm({'name': 'Richard Lockridge'})
       
   273 >>> wf.is_valid()
       
   274 True
       
   275 
       
   276 Replacing a field.
       
   277 
       
   278 >>> class CategoryForm(ModelForm):
       
   279 ...     url = forms.BooleanField()
       
   280 ...
       
   281 ...     class Meta:
       
   282 ...         model = Category
       
   283 
       
   284 >>> CategoryForm.base_fields['url'].__class__
       
   285 <class 'django.forms.fields.BooleanField'>
       
   286 
       
   287 
       
   288 Using 'fields'.
       
   289 
       
   290 >>> class CategoryForm(ModelForm):
       
   291 ...
       
   292 ...     class Meta:
       
   293 ...         model = Category
       
   294 ...         fields = ['url']
       
   295 
       
   296 >>> CategoryForm.base_fields.keys()
       
   297 ['url']
       
   298 
       
   299 
       
   300 Using 'exclude'
       
   301 
       
   302 >>> class CategoryForm(ModelForm):
       
   303 ...
       
   304 ...     class Meta:
       
   305 ...         model = Category
       
   306 ...         exclude = ['url']
       
   307 
       
   308 >>> CategoryForm.base_fields.keys()
       
   309 ['name', 'slug']
       
   310 
       
   311 
       
   312 Using 'fields' *and* 'exclude'. Not sure why you'd want to do this, but uh,
       
   313 "be liberal in what you accept" and all.
       
   314 
       
   315 >>> class CategoryForm(ModelForm):
       
   316 ...
       
   317 ...     class Meta:
       
   318 ...         model = Category
       
   319 ...         fields = ['name', 'url']
       
   320 ...         exclude = ['url']
       
   321 
       
   322 >>> CategoryForm.base_fields.keys()
       
   323 ['name']
       
   324 
       
   325 Using 'widgets'
       
   326 
       
   327 >>> class CategoryForm(ModelForm):
       
   328 ...
       
   329 ...     class Meta:
       
   330 ...         model = Category
       
   331 ...         fields = ['name', 'url', 'slug']
       
   332 ...         widgets = {
       
   333 ...             'name': forms.Textarea,
       
   334 ...             'url': forms.TextInput(attrs={'class': 'url'})
       
   335 ...         }
       
   336 
       
   337 >>> str(CategoryForm()['name'])
       
   338 '<textarea id="id_name" rows="10" cols="40" name="name"></textarea>'
       
   339 
       
   340 >>> str(CategoryForm()['url'])
       
   341 '<input id="id_url" type="text" class="url" name="url" maxlength="40" />'
       
   342 
       
   343 >>> str(CategoryForm()['slug'])
       
   344 '<input id="id_slug" type="text" name="slug" maxlength="20" />'
       
   345 
       
   346 Don't allow more than one 'model' definition in the inheritance hierarchy.
       
   347 Technically, it would generate a valid form, but the fact that the resulting
       
   348 save method won't deal with multiple objects is likely to trip up people not
       
   349 familiar with the mechanics.
       
   350 
       
   351 >>> class CategoryForm(ModelForm):
       
   352 ...     class Meta:
       
   353 ...         model = Category
       
   354 
       
   355 >>> class OddForm(CategoryForm):
       
   356 ...     class Meta:
       
   357 ...         model = Article
       
   358 
       
   359 OddForm is now an Article-related thing, because BadForm.Meta overrides
       
   360 CategoryForm.Meta.
       
   361 >>> OddForm.base_fields.keys()
       
   362 ['headline', 'slug', 'pub_date', 'writer', 'article', 'status', 'categories']
       
   363 
       
   364 >>> class ArticleForm(ModelForm):
       
   365 ...     class Meta:
       
   366 ...         model = Article
       
   367 
       
   368 First class with a Meta class wins.
       
   369 
       
   370 >>> class BadForm(ArticleForm, CategoryForm):
       
   371 ...     pass
       
   372 >>> OddForm.base_fields.keys()
       
   373 ['headline', 'slug', 'pub_date', 'writer', 'article', 'status', 'categories']
       
   374 
       
   375 Subclassing without specifying a Meta on the class will use the parent's Meta
       
   376 (or the first parent in the MRO if there are multiple parent classes).
       
   377 
       
   378 >>> class CategoryForm(ModelForm):
       
   379 ...     class Meta:
       
   380 ...         model = Category
       
   381 >>> class SubCategoryForm(CategoryForm):
       
   382 ...     pass
       
   383 >>> SubCategoryForm.base_fields.keys()
       
   384 ['name', 'slug', 'url']
       
   385 
       
   386 We can also subclass the Meta inner class to change the fields list.
       
   387 
       
   388 >>> class CategoryForm(ModelForm):
       
   389 ...     checkbox = forms.BooleanField()
       
   390 ...
       
   391 ...     class Meta:
       
   392 ...         model = Category
       
   393 >>> class SubCategoryForm(CategoryForm):
       
   394 ...     class Meta(CategoryForm.Meta):
       
   395 ...         exclude = ['url']
       
   396 
       
   397 >>> print SubCategoryForm()
       
   398 <tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr>
       
   399 <tr><th><label for="id_slug">Slug:</label></th><td><input id="id_slug" type="text" name="slug" maxlength="20" /></td></tr>
       
   400 <tr><th><label for="id_checkbox">Checkbox:</label></th><td><input type="checkbox" name="checkbox" id="id_checkbox" /></td></tr>
       
   401 
       
   402 # test using fields to provide ordering to the fields
       
   403 >>> class CategoryForm(ModelForm):
       
   404 ...     class Meta:
       
   405 ...         model = Category
       
   406 ...         fields = ['url', 'name']
       
   407 
       
   408 >>> CategoryForm.base_fields.keys()
       
   409 ['url', 'name']
       
   410 
       
   411 
       
   412 >>> print CategoryForm()
       
   413 <tr><th><label for="id_url">The URL:</label></th><td><input id="id_url" type="text" name="url" maxlength="40" /></td></tr>
       
   414 <tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr>
       
   415 
       
   416 >>> class CategoryForm(ModelForm):
       
   417 ...     class Meta:
       
   418 ...         model = Category
       
   419 ...         fields = ['slug', 'url', 'name']
       
   420 ...         exclude = ['url']
       
   421 
       
   422 >>> CategoryForm.base_fields.keys()
       
   423 ['slug', 'name']
       
   424 
       
   425 # Old form_for_x tests #######################################################
       
   426 
       
   427 >>> from django.forms import ModelForm, CharField
       
   428 >>> import datetime
       
   429 
       
   430 >>> Category.objects.all()
       
   431 []
       
   432 
       
   433 >>> class CategoryForm(ModelForm):
       
   434 ...     class Meta:
       
   435 ...         model = Category
       
   436 >>> f = CategoryForm()
       
   437 >>> print f
       
   438 <tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr>
       
   439 <tr><th><label for="id_slug">Slug:</label></th><td><input id="id_slug" type="text" name="slug" maxlength="20" /></td></tr>
       
   440 <tr><th><label for="id_url">The URL:</label></th><td><input id="id_url" type="text" name="url" maxlength="40" /></td></tr>
       
   441 >>> print f.as_ul()
       
   442 <li><label for="id_name">Name:</label> <input id="id_name" type="text" name="name" maxlength="20" /></li>
       
   443 <li><label for="id_slug">Slug:</label> <input id="id_slug" type="text" name="slug" maxlength="20" /></li>
       
   444 <li><label for="id_url">The URL:</label> <input id="id_url" type="text" name="url" maxlength="40" /></li>
       
   445 >>> print f['name']
       
   446 <input id="id_name" type="text" name="name" maxlength="20" />
       
   447 
       
   448 >>> f = CategoryForm(auto_id=False)
       
   449 >>> print f.as_ul()
       
   450 <li>Name: <input type="text" name="name" maxlength="20" /></li>
       
   451 <li>Slug: <input type="text" name="slug" maxlength="20" /></li>
       
   452 <li>The URL: <input type="text" name="url" maxlength="40" /></li>
       
   453 
       
   454 >>> f = CategoryForm({'name': 'Entertainment', 'slug': 'entertainment', 'url': 'entertainment'})
       
   455 >>> f.is_valid()
       
   456 True
       
   457 >>> f.cleaned_data['url']
       
   458 u'entertainment'
       
   459 >>> f.cleaned_data['name']
       
   460 u'Entertainment'
       
   461 >>> f.cleaned_data['slug']
       
   462 u'entertainment'
       
   463 >>> obj = f.save()
       
   464 >>> obj
       
   465 <Category: Entertainment>
       
   466 >>> Category.objects.all()
       
   467 [<Category: Entertainment>]
       
   468 
       
   469 >>> f = CategoryForm({'name': "It's a test", 'slug': 'its-test', 'url': 'test'})
       
   470 >>> f.is_valid()
       
   471 True
       
   472 >>> f.cleaned_data['url']
       
   473 u'test'
       
   474 >>> f.cleaned_data['name']
       
   475 u"It's a test"
       
   476 >>> f.cleaned_data['slug']
       
   477 u'its-test'
       
   478 >>> obj = f.save()
       
   479 >>> obj
       
   480 <Category: It's a test>
       
   481 >>> Category.objects.order_by('name')
       
   482 [<Category: Entertainment>, <Category: It's a test>]
       
   483 
       
   484 If you call save() with commit=False, then it will return an object that
       
   485 hasn't yet been saved to the database. In this case, it's up to you to call
       
   486 save() on the resulting model instance.
       
   487 >>> f = CategoryForm({'name': 'Third test', 'slug': 'third-test', 'url': 'third'})
       
   488 >>> f.is_valid()
       
   489 True
       
   490 >>> f.cleaned_data['url']
       
   491 u'third'
       
   492 >>> f.cleaned_data['name']
       
   493 u'Third test'
       
   494 >>> f.cleaned_data['slug']
       
   495 u'third-test'
       
   496 >>> obj = f.save(commit=False)
       
   497 >>> obj
       
   498 <Category: Third test>
       
   499 >>> Category.objects.order_by('name')
       
   500 [<Category: Entertainment>, <Category: It's a test>]
       
   501 >>> obj.save()
       
   502 >>> Category.objects.order_by('name')
       
   503 [<Category: Entertainment>, <Category: It's a test>, <Category: Third test>]
       
   504 
       
   505 If you call save() with invalid data, you'll get a ValueError.
       
   506 >>> f = CategoryForm({'name': '', 'slug': 'not a slug!', 'url': 'foo'})
       
   507 >>> f.errors['name']
       
   508 [u'This field is required.']
       
   509 >>> f.errors['slug']
       
   510 [u"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."]
       
   511 >>> f.cleaned_data
       
   512 Traceback (most recent call last):
       
   513 ...
       
   514 AttributeError: 'CategoryForm' object has no attribute 'cleaned_data'
       
   515 >>> f.save()
       
   516 Traceback (most recent call last):
       
   517 ...
       
   518 ValueError: The Category could not be created because the data didn't validate.
       
   519 >>> f = CategoryForm({'name': '', 'slug': '', 'url': 'foo'})
       
   520 >>> f.save()
       
   521 Traceback (most recent call last):
       
   522 ...
       
   523 ValueError: The Category could not be created because the data didn't validate.
       
   524 
       
   525 Create a couple of Writers.
       
   526 >>> w_royko = Writer(name='Mike Royko')
       
   527 >>> w_royko.save()
       
   528 >>> w_woodward = Writer(name='Bob Woodward')
       
   529 >>> w_woodward.save()
       
   530 
       
   531 ManyToManyFields are represented by a MultipleChoiceField, ForeignKeys and any
       
   532 fields with the 'choices' attribute are represented by a ChoiceField.
       
   533 >>> class ArticleForm(ModelForm):
       
   534 ...     class Meta:
       
   535 ...         model = Article
       
   536 >>> f = ArticleForm(auto_id=False)
       
   537 >>> print f
       
   538 <tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
       
   539 <tr><th>Slug:</th><td><input type="text" name="slug" maxlength="50" /></td></tr>
       
   540 <tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr>
       
   541 <tr><th>Writer:</th><td><select name="writer">
       
   542 <option value="" selected="selected">---------</option>
       
   543 <option value="...">Mike Royko</option>
       
   544 <option value="...">Bob Woodward</option>
       
   545 </select></td></tr>
       
   546 <tr><th>Article:</th><td><textarea rows="10" cols="40" name="article"></textarea></td></tr>
       
   547 <tr><th>Status:</th><td><select name="status">
       
   548 <option value="" selected="selected">---------</option>
       
   549 <option value="1">Draft</option>
       
   550 <option value="2">Pending</option>
       
   551 <option value="3">Live</option>
       
   552 </select></td></tr>
       
   553 <tr><th>Categories:</th><td><select multiple="multiple" name="categories">
       
   554 <option value="1">Entertainment</option>
       
   555 <option value="2">It&#39;s a test</option>
       
   556 <option value="3">Third test</option>
       
   557 </select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr>
       
   558 
       
   559 You can restrict a form to a subset of the complete list of fields
       
   560 by providing a 'fields' argument. If you try to save a
       
   561 model created with such a form, you need to ensure that the fields
       
   562 that are _not_ on the form have default values, or are allowed to have
       
   563 a value of None. If a field isn't specified on a form, the object created
       
   564 from the form can't provide a value for that field!
       
   565 >>> class PartialArticleForm(ModelForm):
       
   566 ...     class Meta:
       
   567 ...         model = Article
       
   568 ...         fields = ('headline','pub_date')
       
   569 >>> f = PartialArticleForm(auto_id=False)
       
   570 >>> print f
       
   571 <tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
       
   572 <tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr>
       
   573 
       
   574 When the ModelForm is passed an instance, that instance's current values are
       
   575 inserted as 'initial' data in each Field.
       
   576 >>> w = Writer.objects.get(name='Mike Royko')
       
   577 >>> class RoykoForm(ModelForm):
       
   578 ...     class Meta:
       
   579 ...         model = Writer
       
   580 >>> f = RoykoForm(auto_id=False, instance=w)
       
   581 >>> print f
       
   582 <tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br />Use both first and last names.</td></tr>
       
   583 
       
   584 >>> art = Article(headline='Test article', slug='test-article', pub_date=datetime.date(1988, 1, 4), writer=w, article='Hello.')
       
   585 >>> art.save()
       
   586 >>> art.id
       
   587 1
       
   588 >>> class TestArticleForm(ModelForm):
       
   589 ...     class Meta:
       
   590 ...         model = Article
       
   591 >>> f = TestArticleForm(auto_id=False, instance=art)
       
   592 >>> print f.as_ul()
       
   593 <li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" /></li>
       
   594 <li>Slug: <input type="text" name="slug" value="test-article" maxlength="50" /></li>
       
   595 <li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li>
       
   596 <li>Writer: <select name="writer">
       
   597 <option value="">---------</option>
       
   598 <option value="..." selected="selected">Mike Royko</option>
       
   599 <option value="...">Bob Woodward</option>
       
   600 </select></li>
       
   601 <li>Article: <textarea rows="10" cols="40" name="article">Hello.</textarea></li>
       
   602 <li>Status: <select name="status">
       
   603 <option value="" selected="selected">---------</option>
       
   604 <option value="1">Draft</option>
       
   605 <option value="2">Pending</option>
       
   606 <option value="3">Live</option>
       
   607 </select></li>
       
   608 <li>Categories: <select multiple="multiple" name="categories">
       
   609 <option value="1">Entertainment</option>
       
   610 <option value="2">It&#39;s a test</option>
       
   611 <option value="3">Third test</option>
       
   612 </select>  Hold down "Control", or "Command" on a Mac, to select more than one.</li>
       
   613 >>> f = TestArticleForm({'headline': u'Test headline', 'slug': 'test-headline', 'pub_date': u'1984-02-06', 'writer': unicode(w_royko.pk), 'article': 'Hello.'}, instance=art)
       
   614 >>> f.errors
       
   615 {}
       
   616 >>> f.is_valid()
       
   617 True
       
   618 >>> test_art = f.save()
       
   619 >>> test_art.id
       
   620 1
       
   621 >>> test_art = Article.objects.get(id=1)
       
   622 >>> test_art.headline
       
   623 u'Test headline'
       
   624 
       
   625 You can create a form over a subset of the available fields
       
   626 by specifying a 'fields' argument to form_for_instance.
       
   627 >>> class PartialArticleForm(ModelForm):
       
   628 ...     class Meta:
       
   629 ...         model = Article
       
   630 ...         fields=('headline', 'slug', 'pub_date')
       
   631 >>> f = PartialArticleForm({'headline': u'New headline', 'slug': 'new-headline', 'pub_date': u'1988-01-04'}, auto_id=False, instance=art)
       
   632 >>> print f.as_ul()
       
   633 <li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
       
   634 <li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li>
       
   635 <li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li>
       
   636 >>> f.is_valid()
       
   637 True
       
   638 >>> new_art = f.save()
       
   639 >>> new_art.id
       
   640 1
       
   641 >>> new_art = Article.objects.get(id=1)
       
   642 >>> new_art.headline
       
   643 u'New headline'
       
   644 
       
   645 Add some categories and test the many-to-many form output.
       
   646 >>> new_art.categories.all()
       
   647 []
       
   648 >>> new_art.categories.add(Category.objects.get(name='Entertainment'))
       
   649 >>> new_art.categories.all()
       
   650 [<Category: Entertainment>]
       
   651 >>> class TestArticleForm(ModelForm):
       
   652 ...     class Meta:
       
   653 ...         model = Article
       
   654 >>> f = TestArticleForm(auto_id=False, instance=new_art)
       
   655 >>> print f.as_ul()
       
   656 <li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
       
   657 <li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li>
       
   658 <li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li>
       
   659 <li>Writer: <select name="writer">
       
   660 <option value="">---------</option>
       
   661 <option value="..." selected="selected">Mike Royko</option>
       
   662 <option value="...">Bob Woodward</option>
       
   663 </select></li>
       
   664 <li>Article: <textarea rows="10" cols="40" name="article">Hello.</textarea></li>
       
   665 <li>Status: <select name="status">
       
   666 <option value="" selected="selected">---------</option>
       
   667 <option value="1">Draft</option>
       
   668 <option value="2">Pending</option>
       
   669 <option value="3">Live</option>
       
   670 </select></li>
       
   671 <li>Categories: <select multiple="multiple" name="categories">
       
   672 <option value="1" selected="selected">Entertainment</option>
       
   673 <option value="2">It&#39;s a test</option>
       
   674 <option value="3">Third test</option>
       
   675 </select>  Hold down "Control", or "Command" on a Mac, to select more than one.</li>
       
   676 
       
   677 Initial values can be provided for model forms
       
   678 >>> f = TestArticleForm(auto_id=False, initial={'headline': 'Your headline here', 'categories': ['1','2']})
       
   679 >>> print f.as_ul()
       
   680 <li>Headline: <input type="text" name="headline" value="Your headline here" maxlength="50" /></li>
       
   681 <li>Slug: <input type="text" name="slug" maxlength="50" /></li>
       
   682 <li>Pub date: <input type="text" name="pub_date" /></li>
       
   683 <li>Writer: <select name="writer">
       
   684 <option value="" selected="selected">---------</option>
       
   685 <option value="...">Mike Royko</option>
       
   686 <option value="...">Bob Woodward</option>
       
   687 </select></li>
       
   688 <li>Article: <textarea rows="10" cols="40" name="article"></textarea></li>
       
   689 <li>Status: <select name="status">
       
   690 <option value="" selected="selected">---------</option>
       
   691 <option value="1">Draft</option>
       
   692 <option value="2">Pending</option>
       
   693 <option value="3">Live</option>
       
   694 </select></li>
       
   695 <li>Categories: <select multiple="multiple" name="categories">
       
   696 <option value="1" selected="selected">Entertainment</option>
       
   697 <option value="2" selected="selected">It&#39;s a test</option>
       
   698 <option value="3">Third test</option>
       
   699 </select>  Hold down "Control", or "Command" on a Mac, to select more than one.</li>
       
   700 
       
   701 >>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
       
   702 ...     'writer': unicode(w_royko.pk), 'article': u'Hello.', 'categories': [u'1', u'2']}, instance=new_art)
       
   703 >>> new_art = f.save()
       
   704 >>> new_art.id
       
   705 1
       
   706 >>> new_art = Article.objects.get(id=1)
       
   707 >>> new_art.categories.order_by('name')
       
   708 [<Category: Entertainment>, <Category: It's a test>]
       
   709 
       
   710 Now, submit form data with no categories. This deletes the existing categories.
       
   711 >>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
       
   712 ...     'writer': unicode(w_royko.pk), 'article': u'Hello.'}, instance=new_art)
       
   713 >>> new_art = f.save()
       
   714 >>> new_art.id
       
   715 1
       
   716 >>> new_art = Article.objects.get(id=1)
       
   717 >>> new_art.categories.all()
       
   718 []
       
   719 
       
   720 Create a new article, with categories, via the form.
       
   721 >>> class ArticleForm(ModelForm):
       
   722 ...     class Meta:
       
   723 ...         model = Article
       
   724 >>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
       
   725 ...     'writer': unicode(w_royko.pk), 'article': u'Test.', 'categories': [u'1', u'2']})
       
   726 >>> new_art = f.save()
       
   727 >>> new_art.id
       
   728 2
       
   729 >>> new_art = Article.objects.get(id=2)
       
   730 >>> new_art.categories.order_by('name')
       
   731 [<Category: Entertainment>, <Category: It's a test>]
       
   732 
       
   733 Create a new article, with no categories, via the form.
       
   734 >>> class ArticleForm(ModelForm):
       
   735 ...     class Meta:
       
   736 ...         model = Article
       
   737 >>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
       
   738 ...     'writer': unicode(w_royko.pk), 'article': u'Test.'})
       
   739 >>> new_art = f.save()
       
   740 >>> new_art.id
       
   741 3
       
   742 >>> new_art = Article.objects.get(id=3)
       
   743 >>> new_art.categories.all()
       
   744 []
       
   745 
       
   746 Create a new article, with categories, via the form, but use commit=False.
       
   747 The m2m data won't be saved until save_m2m() is invoked on the form.
       
   748 >>> class ArticleForm(ModelForm):
       
   749 ...     class Meta:
       
   750 ...         model = Article
       
   751 >>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': 'walrus-was-paul', 'pub_date': u'1967-11-01',
       
   752 ...     'writer': unicode(w_royko.pk), 'article': u'Test.', 'categories': [u'1', u'2']})
       
   753 >>> new_art = f.save(commit=False)
       
   754 
       
   755 # Manually save the instance
       
   756 >>> new_art.save()
       
   757 >>> new_art.id
       
   758 4
       
   759 
       
   760 # The instance doesn't have m2m data yet
       
   761 >>> new_art = Article.objects.get(id=4)
       
   762 >>> new_art.categories.all()
       
   763 []
       
   764 
       
   765 # Save the m2m data on the form
       
   766 >>> f.save_m2m()
       
   767 >>> new_art.categories.order_by('name')
       
   768 [<Category: Entertainment>, <Category: It's a test>]
       
   769 
       
   770 Here, we define a custom ModelForm. Because it happens to have the same fields as
       
   771 the Category model, we can just call the form's save() to apply its changes to an
       
   772 existing Category instance.
       
   773 >>> class ShortCategory(ModelForm):
       
   774 ...     name = CharField(max_length=5)
       
   775 ...     slug = CharField(max_length=5)
       
   776 ...     url = CharField(max_length=3)
       
   777 >>> cat = Category.objects.get(name='Third test')
       
   778 >>> cat
       
   779 <Category: Third test>
       
   780 >>> cat.id
       
   781 3
       
   782 >>> form = ShortCategory({'name': 'Third', 'slug': 'third', 'url': '3rd'}, instance=cat)
       
   783 >>> form.save()
       
   784 <Category: Third>
       
   785 >>> Category.objects.get(id=3)
       
   786 <Category: Third>
       
   787 
       
   788 Here, we demonstrate that choices for a ForeignKey ChoiceField are determined
       
   789 at runtime, based on the data in the database when the form is displayed, not
       
   790 the data in the database when the form is instantiated.
       
   791 >>> class ArticleForm(ModelForm):
       
   792 ...     class Meta:
       
   793 ...         model = Article
       
   794 >>> f = ArticleForm(auto_id=False)
       
   795 >>> print f.as_ul()
       
   796 <li>Headline: <input type="text" name="headline" maxlength="50" /></li>
       
   797 <li>Slug: <input type="text" name="slug" maxlength="50" /></li>
       
   798 <li>Pub date: <input type="text" name="pub_date" /></li>
       
   799 <li>Writer: <select name="writer">
       
   800 <option value="" selected="selected">---------</option>
       
   801 <option value="...">Mike Royko</option>
       
   802 <option value="...">Bob Woodward</option>
       
   803 </select></li>
       
   804 <li>Article: <textarea rows="10" cols="40" name="article"></textarea></li>
       
   805 <li>Status: <select name="status">
       
   806 <option value="" selected="selected">---------</option>
       
   807 <option value="1">Draft</option>
       
   808 <option value="2">Pending</option>
       
   809 <option value="3">Live</option>
       
   810 </select></li>
       
   811 <li>Categories: <select multiple="multiple" name="categories">
       
   812 <option value="1">Entertainment</option>
       
   813 <option value="2">It&#39;s a test</option>
       
   814 <option value="3">Third</option>
       
   815 </select>  Hold down "Control", or "Command" on a Mac, to select more than one.</li>
       
   816 >>> Category.objects.create(name='Fourth', url='4th')
       
   817 <Category: Fourth>
       
   818 >>> Writer.objects.create(name='Carl Bernstein')
       
   819 <Writer: Carl Bernstein>
       
   820 >>> print f.as_ul()
       
   821 <li>Headline: <input type="text" name="headline" maxlength="50" /></li>
       
   822 <li>Slug: <input type="text" name="slug" maxlength="50" /></li>
       
   823 <li>Pub date: <input type="text" name="pub_date" /></li>
       
   824 <li>Writer: <select name="writer">
       
   825 <option value="" selected="selected">---------</option>
       
   826 <option value="...">Mike Royko</option>
       
   827 <option value="...">Bob Woodward</option>
       
   828 <option value="...">Carl Bernstein</option>
       
   829 </select></li>
       
   830 <li>Article: <textarea rows="10" cols="40" name="article"></textarea></li>
       
   831 <li>Status: <select name="status">
       
   832 <option value="" selected="selected">---------</option>
       
   833 <option value="1">Draft</option>
       
   834 <option value="2">Pending</option>
       
   835 <option value="3">Live</option>
       
   836 </select></li>
       
   837 <li>Categories: <select multiple="multiple" name="categories">
       
   838 <option value="1">Entertainment</option>
       
   839 <option value="2">It&#39;s a test</option>
       
   840 <option value="3">Third</option>
       
   841 <option value="4">Fourth</option>
       
   842 </select>  Hold down "Control", or "Command" on a Mac, to select more than one.</li>
       
   843 
       
   844 # ModelChoiceField ############################################################
       
   845 
       
   846 >>> from django.forms import ModelChoiceField, ModelMultipleChoiceField
       
   847 
       
   848 >>> f = ModelChoiceField(Category.objects.all())
       
   849 >>> list(f.choices)
       
   850 [(u'', u'---------'), (1, u'Entertainment'), (2, u"It's a test"), (3, u'Third'), (4, u'Fourth')]
       
   851 >>> f.clean('')
       
   852 Traceback (most recent call last):
       
   853 ...
       
   854 ValidationError: [u'This field is required.']
       
   855 >>> f.clean(None)
       
   856 Traceback (most recent call last):
       
   857 ...
       
   858 ValidationError: [u'This field is required.']
       
   859 >>> f.clean(0)
       
   860 Traceback (most recent call last):
       
   861 ...
       
   862 ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
       
   863 >>> f.clean(3)
       
   864 <Category: Third>
       
   865 >>> f.clean(2)
       
   866 <Category: It's a test>
       
   867 
       
   868 # Add a Category object *after* the ModelChoiceField has already been
       
   869 # instantiated. This proves clean() checks the database during clean() rather
       
   870 # than caching it at time of instantiation.
       
   871 >>> Category.objects.create(name='Fifth', url='5th')
       
   872 <Category: Fifth>
       
   873 >>> f.clean(5)
       
   874 <Category: Fifth>
       
   875 
       
   876 # Delete a Category object *after* the ModelChoiceField has already been
       
   877 # instantiated. This proves clean() checks the database during clean() rather
       
   878 # than caching it at time of instantiation.
       
   879 >>> Category.objects.get(url='5th').delete()
       
   880 >>> f.clean(5)
       
   881 Traceback (most recent call last):
       
   882 ...
       
   883 ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
       
   884 
       
   885 >>> f = ModelChoiceField(Category.objects.filter(pk=1), required=False)
       
   886 >>> print f.clean('')
       
   887 None
       
   888 >>> f.clean('')
       
   889 >>> f.clean('1')
       
   890 <Category: Entertainment>
       
   891 >>> f.clean('100')
       
   892 Traceback (most recent call last):
       
   893 ...
       
   894 ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
       
   895 
       
   896 # queryset can be changed after the field is created.
       
   897 >>> f.queryset = Category.objects.exclude(name='Fourth')
       
   898 >>> list(f.choices)
       
   899 [(u'', u'---------'), (1, u'Entertainment'), (2, u"It's a test"), (3, u'Third')]
       
   900 >>> f.clean(3)
       
   901 <Category: Third>
       
   902 >>> f.clean(4)
       
   903 Traceback (most recent call last):
       
   904 ...
       
   905 ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
       
   906 
       
   907 # check that we can safely iterate choices repeatedly
       
   908 >>> gen_one = list(f.choices)
       
   909 >>> gen_two = f.choices
       
   910 >>> gen_one[2]
       
   911 (2L, u"It's a test")
       
   912 >>> list(gen_two)
       
   913 [(u'', u'---------'), (1L, u'Entertainment'), (2L, u"It's a test"), (3L, u'Third')]
       
   914 
       
   915 # check that we can override the label_from_instance method to print custom labels (#4620)
       
   916 >>> f.queryset = Category.objects.all()
       
   917 >>> f.label_from_instance = lambda obj: "category " + str(obj)
       
   918 >>> list(f.choices)
       
   919 [(u'', u'---------'), (1L, 'category Entertainment'), (2L, "category It's a test"), (3L, 'category Third'), (4L, 'category Fourth')]
       
   920 
       
   921 # ModelMultipleChoiceField ####################################################
       
   922 
       
   923 >>> f = ModelMultipleChoiceField(Category.objects.all())
       
   924 >>> list(f.choices)
       
   925 [(1, u'Entertainment'), (2, u"It's a test"), (3, u'Third'), (4, u'Fourth')]
       
   926 >>> f.clean(None)
       
   927 Traceback (most recent call last):
       
   928 ...
       
   929 ValidationError: [u'This field is required.']
       
   930 >>> f.clean([])
       
   931 Traceback (most recent call last):
       
   932 ...
       
   933 ValidationError: [u'This field is required.']
       
   934 >>> f.clean([1])
       
   935 [<Category: Entertainment>]
       
   936 >>> f.clean([2])
       
   937 [<Category: It's a test>]
       
   938 >>> f.clean(['1'])
       
   939 [<Category: Entertainment>]
       
   940 >>> f.clean(['1', '2'])
       
   941 [<Category: Entertainment>, <Category: It's a test>]
       
   942 >>> f.clean([1, '2'])
       
   943 [<Category: Entertainment>, <Category: It's a test>]
       
   944 >>> f.clean((1, '2'))
       
   945 [<Category: Entertainment>, <Category: It's a test>]
       
   946 >>> f.clean(['100'])
       
   947 Traceback (most recent call last):
       
   948 ...
       
   949 ValidationError: [u'Select a valid choice. 100 is not one of the available choices.']
       
   950 >>> f.clean('hello')
       
   951 Traceback (most recent call last):
       
   952 ...
       
   953 ValidationError: [u'Enter a list of values.']
       
   954 >>> f.clean(['fail'])
       
   955 Traceback (most recent call last):
       
   956 ...
       
   957 ValidationError: [u'"fail" is not a valid value for a primary key.']
       
   958 
       
   959 # Add a Category object *after* the ModelMultipleChoiceField has already been
       
   960 # instantiated. This proves clean() checks the database during clean() rather
       
   961 # than caching it at time of instantiation.
       
   962 >>> Category.objects.create(id=6, name='Sixth', url='6th')
       
   963 <Category: Sixth>
       
   964 >>> f.clean([6])
       
   965 [<Category: Sixth>]
       
   966 
       
   967 # Delete a Category object *after* the ModelMultipleChoiceField has already been
       
   968 # instantiated. This proves clean() checks the database during clean() rather
       
   969 # than caching it at time of instantiation.
       
   970 >>> Category.objects.get(url='6th').delete()
       
   971 >>> f.clean([6])
       
   972 Traceback (most recent call last):
       
   973 ...
       
   974 ValidationError: [u'Select a valid choice. 6 is not one of the available choices.']
       
   975 
       
   976 >>> f = ModelMultipleChoiceField(Category.objects.all(), required=False)
       
   977 >>> f.clean([])
       
   978 []
       
   979 >>> f.clean(())
       
   980 []
       
   981 >>> f.clean(['10'])
       
   982 Traceback (most recent call last):
       
   983 ...
       
   984 ValidationError: [u'Select a valid choice. 10 is not one of the available choices.']
       
   985 >>> f.clean(['3', '10'])
       
   986 Traceback (most recent call last):
       
   987 ...
       
   988 ValidationError: [u'Select a valid choice. 10 is not one of the available choices.']
       
   989 >>> f.clean(['1', '10'])
       
   990 Traceback (most recent call last):
       
   991 ...
       
   992 ValidationError: [u'Select a valid choice. 10 is not one of the available choices.']
       
   993 
       
   994 # queryset can be changed after the field is created.
       
   995 >>> f.queryset = Category.objects.exclude(name='Fourth')
       
   996 >>> list(f.choices)
       
   997 [(1, u'Entertainment'), (2, u"It's a test"), (3, u'Third')]
       
   998 >>> f.clean([3])
       
   999 [<Category: Third>]
       
  1000 >>> f.clean([4])
       
  1001 Traceback (most recent call last):
       
  1002 ...
       
  1003 ValidationError: [u'Select a valid choice. 4 is not one of the available choices.']
       
  1004 >>> f.clean(['3', '4'])
       
  1005 Traceback (most recent call last):
       
  1006 ...
       
  1007 ValidationError: [u'Select a valid choice. 4 is not one of the available choices.']
       
  1008 
       
  1009 >>> f.queryset = Category.objects.all()
       
  1010 >>> f.label_from_instance = lambda obj: "multicategory " + str(obj)
       
  1011 >>> list(f.choices)
       
  1012 [(1L, 'multicategory Entertainment'), (2L, "multicategory It's a test"), (3L, 'multicategory Third'), (4L, 'multicategory Fourth')]
       
  1013 
       
  1014 # OneToOneField ###############################################################
       
  1015 
       
  1016 >>> class ImprovedArticleForm(ModelForm):
       
  1017 ...     class Meta:
       
  1018 ...         model = ImprovedArticle
       
  1019 >>> ImprovedArticleForm.base_fields.keys()
       
  1020 ['article']
       
  1021 
       
  1022 >>> class ImprovedArticleWithParentLinkForm(ModelForm):
       
  1023 ...     class Meta:
       
  1024 ...         model = ImprovedArticleWithParentLink
       
  1025 >>> ImprovedArticleWithParentLinkForm.base_fields.keys()
       
  1026 []
       
  1027 
       
  1028 >>> bw = BetterWriter(name=u'Joe Better', score=10)
       
  1029 >>> bw.save()
       
  1030 >>> sorted(model_to_dict(bw).keys())
       
  1031 ['id', 'name', 'score', 'writer_ptr']
       
  1032 
       
  1033 >>> class BetterWriterForm(ModelForm):
       
  1034 ...     class Meta:
       
  1035 ...         model = BetterWriter
       
  1036 >>> form = BetterWriterForm({'name': 'Some Name', 'score': 12})
       
  1037 >>> form.is_valid()
       
  1038 True
       
  1039 >>> bw2 = form.save()
       
  1040 >>> bw2.delete()
       
  1041 
       
  1042 
       
  1043 >>> class WriterProfileForm(ModelForm):
       
  1044 ...     class Meta:
       
  1045 ...         model = WriterProfile
       
  1046 >>> form = WriterProfileForm()
       
  1047 >>> print form.as_p()
       
  1048 <p><label for="id_writer">Writer:</label> <select name="writer" id="id_writer">
       
  1049 <option value="" selected="selected">---------</option>
       
  1050 <option value="...">Mike Royko</option>
       
  1051 <option value="...">Bob Woodward</option>
       
  1052 <option value="...">Carl Bernstein</option>
       
  1053 <option value="...">Joe Better</option>
       
  1054 </select></p>
       
  1055 <p><label for="id_age">Age:</label> <input type="text" name="age" id="id_age" /></p>
       
  1056 
       
  1057 >>> data = {
       
  1058 ...     'writer': unicode(w_woodward.pk),
       
  1059 ...     'age': u'65',
       
  1060 ... }
       
  1061 >>> form = WriterProfileForm(data)
       
  1062 >>> instance = form.save()
       
  1063 >>> instance
       
  1064 <WriterProfile: Bob Woodward is 65>
       
  1065 
       
  1066 >>> form = WriterProfileForm(instance=instance)
       
  1067 >>> print form.as_p()
       
  1068 <p><label for="id_writer">Writer:</label> <select name="writer" id="id_writer">
       
  1069 <option value="">---------</option>
       
  1070 <option value="...">Mike Royko</option>
       
  1071 <option value="..." selected="selected">Bob Woodward</option>
       
  1072 <option value="...">Carl Bernstein</option>
       
  1073 <option value="...">Joe Better</option>
       
  1074 </select></p>
       
  1075 <p><label for="id_age">Age:</label> <input type="text" name="age" value="65" id="id_age" /></p>
       
  1076 
       
  1077 # PhoneNumberField ############################################################
       
  1078 
       
  1079 >>> class PhoneNumberForm(ModelForm):
       
  1080 ...     class Meta:
       
  1081 ...         model = PhoneNumber
       
  1082 >>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'})
       
  1083 >>> f.is_valid()
       
  1084 True
       
  1085 >>> f.cleaned_data['phone']
       
  1086 u'312-555-1212'
       
  1087 >>> f.cleaned_data['description']
       
  1088 u'Assistance'
       
  1089 
       
  1090 # FileField ###################################################################
       
  1091 
       
  1092 # File forms.
       
  1093 
       
  1094 >>> class TextFileForm(ModelForm):
       
  1095 ...     class Meta:
       
  1096 ...         model = TextFile
       
  1097 
       
  1098 # Test conditions when files is either not given or empty.
       
  1099 
       
  1100 >>> f = TextFileForm(data={'description': u'Assistance'})
       
  1101 >>> f.is_valid()
       
  1102 False
       
  1103 >>> f = TextFileForm(data={'description': u'Assistance'}, files={})
       
  1104 >>> f.is_valid()
       
  1105 False
       
  1106 
       
  1107 # Upload a file and ensure it all works as expected.
       
  1108 
       
  1109 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test1.txt', 'hello world')})
       
  1110 >>> f.is_valid()
       
  1111 True
       
  1112 >>> type(f.cleaned_data['file'])
       
  1113 <class 'django.core.files.uploadedfile.SimpleUploadedFile'>
       
  1114 >>> instance = f.save()
       
  1115 >>> instance.file
       
  1116 <FieldFile: tests/test1.txt>
       
  1117 
       
  1118 >>> instance.file.delete()
       
  1119 
       
  1120 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test1.txt', 'hello world')})
       
  1121 >>> f.is_valid()
       
  1122 True
       
  1123 >>> type(f.cleaned_data['file'])
       
  1124 <class 'django.core.files.uploadedfile.SimpleUploadedFile'>
       
  1125 >>> instance = f.save()
       
  1126 >>> instance.file
       
  1127 <FieldFile: tests/test1.txt>
       
  1128 
       
  1129 # Check if the max_length attribute has been inherited from the model.
       
  1130 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test-maxlength.txt', 'hello world')})
       
  1131 >>> f.is_valid()
       
  1132 False
       
  1133 
       
  1134 # Edit an instance that already has the file defined in the model. This will not
       
  1135 # save the file again, but leave it exactly as it is.
       
  1136 
       
  1137 >>> f = TextFileForm(data={'description': u'Assistance'}, instance=instance)
       
  1138 >>> f.is_valid()
       
  1139 True
       
  1140 >>> f.cleaned_data['file']
       
  1141 <FieldFile: tests/test1.txt>
       
  1142 >>> instance = f.save()
       
  1143 >>> instance.file
       
  1144 <FieldFile: tests/test1.txt>
       
  1145 
       
  1146 # Delete the current file since this is not done by Django.
       
  1147 >>> instance.file.delete()
       
  1148 
       
  1149 # Override the file by uploading a new one.
       
  1150 
       
  1151 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test2.txt', 'hello world')}, instance=instance)
       
  1152 >>> f.is_valid()
       
  1153 True
       
  1154 >>> instance = f.save()
       
  1155 >>> instance.file
       
  1156 <FieldFile: tests/test2.txt>
       
  1157 
       
  1158 # Delete the current file since this is not done by Django.
       
  1159 >>> instance.file.delete()
       
  1160 
       
  1161 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test2.txt', 'hello world')})
       
  1162 >>> f.is_valid()
       
  1163 True
       
  1164 >>> instance = f.save()
       
  1165 >>> instance.file
       
  1166 <FieldFile: tests/test2.txt>
       
  1167 
       
  1168 # Delete the current file since this is not done by Django.
       
  1169 >>> instance.file.delete()
       
  1170 
       
  1171 >>> instance.delete()
       
  1172 
       
  1173 # Test the non-required FileField
       
  1174 >>> f = TextFileForm(data={'description': u'Assistance'})
       
  1175 >>> f.fields['file'].required = False
       
  1176 >>> f.is_valid()
       
  1177 True
       
  1178 >>> instance = f.save()
       
  1179 >>> instance.file
       
  1180 <FieldFile: None>
       
  1181 
       
  1182 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test3.txt', 'hello world')}, instance=instance)
       
  1183 >>> f.is_valid()
       
  1184 True
       
  1185 >>> instance = f.save()
       
  1186 >>> instance.file
       
  1187 <FieldFile: tests/test3.txt>
       
  1188 
       
  1189 # Instance can be edited w/out re-uploading the file and existing file should be preserved.
       
  1190 
       
  1191 >>> f = TextFileForm(data={'description': u'New Description'}, instance=instance)
       
  1192 >>> f.fields['file'].required = False
       
  1193 >>> f.is_valid()
       
  1194 True
       
  1195 >>> instance = f.save()
       
  1196 >>> instance.description
       
  1197 u'New Description'
       
  1198 >>> instance.file
       
  1199 <FieldFile: tests/test3.txt>
       
  1200 
       
  1201 # Delete the current file since this is not done by Django.
       
  1202 >>> instance.file.delete()
       
  1203 >>> instance.delete()
       
  1204 
       
  1205 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test3.txt', 'hello world')})
       
  1206 >>> f.is_valid()
       
  1207 True
       
  1208 >>> instance = f.save()
       
  1209 >>> instance.file
       
  1210 <FieldFile: tests/test3.txt>
       
  1211 
       
  1212 # Delete the current file since this is not done by Django.
       
  1213 >>> instance.file.delete()
       
  1214 >>> instance.delete()
       
  1215 
       
  1216 # BigIntegerField ################################################################
       
  1217 >>> class BigIntForm(forms.ModelForm):
       
  1218 ...     class Meta:
       
  1219 ...         model = BigInt
       
  1220 ...
       
  1221 >>> bif = BigIntForm({'biggie': '-9223372036854775808'})
       
  1222 >>> bif.is_valid()
       
  1223 True
       
  1224 >>> bif = BigIntForm({'biggie': '-9223372036854775809'})
       
  1225 >>> bif.is_valid()
       
  1226 False
       
  1227 >>> bif.errors
       
  1228 {'biggie': [u'Ensure this value is greater than or equal to -9223372036854775808.']}
       
  1229 >>> bif = BigIntForm({'biggie': '9223372036854775807'})
       
  1230 >>> bif.is_valid()
       
  1231 True
       
  1232 >>> bif = BigIntForm({'biggie': '9223372036854775808'})
       
  1233 >>> bif.is_valid()
       
  1234 False
       
  1235 >>> bif.errors
       
  1236 {'biggie': [u'Ensure this value is less than or equal to 9223372036854775807.']}
       
  1237 """}
       
  1238 
       
  1239 if test_images:
       
  1240     __test__['API_TESTS'] += """
       
  1241 # ImageField ###################################################################
       
  1242 
       
  1243 # ImageField and FileField are nearly identical, but they differ slighty when
       
  1244 # it comes to validation. This specifically tests that #6302 is fixed for
       
  1245 # both file fields and image fields.
       
  1246 
       
  1247 >>> class ImageFileForm(ModelForm):
       
  1248 ...     class Meta:
       
  1249 ...         model = ImageFile
       
  1250 
       
  1251 >>> image_data = open(os.path.join(os.path.dirname(__file__), "test.png"), 'rb').read()
       
  1252 >>> image_data2 = open(os.path.join(os.path.dirname(__file__), "test2.png"), 'rb').read()
       
  1253 
       
  1254 >>> f = ImageFileForm(data={'description': u'An image'}, files={'image': SimpleUploadedFile('test.png', image_data)})
       
  1255 >>> f.is_valid()
       
  1256 True
       
  1257 >>> type(f.cleaned_data['image'])
       
  1258 <class 'django.core.files.uploadedfile.SimpleUploadedFile'>
       
  1259 >>> instance = f.save()
       
  1260 >>> instance.image
       
  1261 <...FieldFile: tests/test.png>
       
  1262 >>> instance.width
       
  1263 16
       
  1264 >>> instance.height
       
  1265 16
       
  1266 
       
  1267 # Delete the current file since this is not done by Django, but don't save
       
  1268 # because the dimension fields are not null=True.
       
  1269 >>> instance.image.delete(save=False)
       
  1270 
       
  1271 >>> f = ImageFileForm(data={'description': u'An image'}, files={'image': SimpleUploadedFile('test.png', image_data)})
       
  1272 >>> f.is_valid()
       
  1273 True
       
  1274 >>> type(f.cleaned_data['image'])
       
  1275 <class 'django.core.files.uploadedfile.SimpleUploadedFile'>
       
  1276 >>> instance = f.save()
       
  1277 >>> instance.image
       
  1278 <...FieldFile: tests/test.png>
       
  1279 >>> instance.width
       
  1280 16
       
  1281 >>> instance.height
       
  1282 16
       
  1283 
       
  1284 # Edit an instance that already has the (required) image defined in the model. This will not
       
  1285 # save the image again, but leave it exactly as it is.
       
  1286 
       
  1287 >>> f = ImageFileForm(data={'description': u'Look, it changed'}, instance=instance)
       
  1288 >>> f.is_valid()
       
  1289 True
       
  1290 >>> f.cleaned_data['image']
       
  1291 <...FieldFile: tests/test.png>
       
  1292 >>> instance = f.save()
       
  1293 >>> instance.image
       
  1294 <...FieldFile: tests/test.png>
       
  1295 >>> instance.height
       
  1296 16
       
  1297 >>> instance.width
       
  1298 16
       
  1299 
       
  1300 # Delete the current file since this is not done by Django, but don't save
       
  1301 # because the dimension fields are not null=True.
       
  1302 >>> instance.image.delete(save=False)
       
  1303 
       
  1304 # Override the file by uploading a new one.
       
  1305 
       
  1306 >>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': SimpleUploadedFile('test2.png', image_data2)}, instance=instance)
       
  1307 >>> f.is_valid()
       
  1308 True
       
  1309 >>> instance = f.save()
       
  1310 >>> instance.image
       
  1311 <...FieldFile: tests/test2.png>
       
  1312 >>> instance.height
       
  1313 32
       
  1314 >>> instance.width
       
  1315 48
       
  1316 
       
  1317 # Delete the current file since this is not done by Django, but don't save
       
  1318 # because the dimension fields are not null=True.
       
  1319 >>> instance.image.delete(save=False)
       
  1320 >>> instance.delete()
       
  1321 
       
  1322 >>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': SimpleUploadedFile('test2.png', image_data2)})
       
  1323 >>> f.is_valid()
       
  1324 True
       
  1325 >>> instance = f.save()
       
  1326 >>> instance.image
       
  1327 <...FieldFile: tests/test2.png>
       
  1328 >>> instance.height
       
  1329 32
       
  1330 >>> instance.width
       
  1331 48
       
  1332 
       
  1333 # Delete the current file since this is not done by Django, but don't save
       
  1334 # because the dimension fields are not null=True.
       
  1335 >>> instance.image.delete(save=False)
       
  1336 >>> instance.delete()
       
  1337 
       
  1338 # Test the non-required ImageField
       
  1339 
       
  1340 >>> class OptionalImageFileForm(ModelForm):
       
  1341 ...     class Meta:
       
  1342 ...         model = OptionalImageFile
       
  1343 
       
  1344 >>> f = OptionalImageFileForm(data={'description': u'Test'})
       
  1345 >>> f.is_valid()
       
  1346 True
       
  1347 >>> instance = f.save()
       
  1348 >>> instance.image
       
  1349 <...FieldFile: None>
       
  1350 >>> instance.width
       
  1351 >>> instance.height
       
  1352 
       
  1353 >>> f = OptionalImageFileForm(data={'description': u'And a final one'}, files={'image': SimpleUploadedFile('test3.png', image_data)}, instance=instance)
       
  1354 >>> f.is_valid()
       
  1355 True
       
  1356 >>> instance = f.save()
       
  1357 >>> instance.image
       
  1358 <...FieldFile: tests/test3.png>
       
  1359 >>> instance.width
       
  1360 16
       
  1361 >>> instance.height
       
  1362 16
       
  1363 
       
  1364 # Editing the instance without re-uploading the image should not affect the image or its width/height properties
       
  1365 >>> f = OptionalImageFileForm(data={'description': u'New Description'}, instance=instance)
       
  1366 >>> f.is_valid()
       
  1367 True
       
  1368 >>> instance = f.save()
       
  1369 >>> instance.description
       
  1370 u'New Description'
       
  1371 >>> instance.image
       
  1372 <...FieldFile: tests/test3.png>
       
  1373 >>> instance.width
       
  1374 16
       
  1375 >>> instance.height
       
  1376 16
       
  1377 
       
  1378 # Delete the current file since this is not done by Django.
       
  1379 >>> instance.image.delete()
       
  1380 >>> instance.delete()
       
  1381 
       
  1382 >>> f = OptionalImageFileForm(data={'description': u'And a final one'}, files={'image': SimpleUploadedFile('test4.png', image_data2)})
       
  1383 >>> f.is_valid()
       
  1384 True
       
  1385 >>> instance = f.save()
       
  1386 >>> instance.image
       
  1387 <...FieldFile: tests/test4.png>
       
  1388 >>> instance.width
       
  1389 48
       
  1390 >>> instance.height
       
  1391 32
       
  1392 >>> instance.delete()
       
  1393 
       
  1394 # Test callable upload_to behavior that's dependent on the value of another field in the model
       
  1395 >>> f = ImageFileForm(data={'description': u'And a final one', 'path': 'foo'}, files={'image': SimpleUploadedFile('test4.png', image_data)})
       
  1396 >>> f.is_valid()
       
  1397 True
       
  1398 >>> instance = f.save()
       
  1399 >>> instance.image
       
  1400 <...FieldFile: foo/test4.png>
       
  1401 >>> instance.delete()
       
  1402 """
       
  1403 
       
  1404 __test__['API_TESTS'] += """
       
  1405 
       
  1406 # Media on a ModelForm ########################################################
       
  1407 
       
  1408 # Similar to a regular Form class you can define custom media to be used on
       
  1409 # the ModelForm.
       
  1410 
       
  1411 >>> class ModelFormWithMedia(ModelForm):
       
  1412 ...     class Media:
       
  1413 ...         js = ('/some/form/javascript',)
       
  1414 ...         css = {
       
  1415 ...             'all': ('/some/form/css',)
       
  1416 ...         }
       
  1417 ...     class Meta:
       
  1418 ...         model = PhoneNumber
       
  1419 >>> f = ModelFormWithMedia()
       
  1420 >>> print f.media
       
  1421 <link href="/some/form/css" type="text/css" media="all" rel="stylesheet" />
       
  1422 <script type="text/javascript" src="/some/form/javascript"></script>
       
  1423 
       
  1424 >>> class CommaSeparatedIntegerForm(ModelForm):
       
  1425 ...    class Meta:
       
  1426 ...        model = CommaSeparatedInteger
       
  1427 
       
  1428 >>> f = CommaSeparatedIntegerForm({'field': '1,2,3'})
       
  1429 >>> f.is_valid()
       
  1430 True
       
  1431 >>> f.cleaned_data
       
  1432 {'field': u'1,2,3'}
       
  1433 >>> f = CommaSeparatedIntegerForm({'field': '1a,2'})
       
  1434 >>> f.errors
       
  1435 {'field': [u'Enter only digits separated by commas.']}
       
  1436 >>> f = CommaSeparatedIntegerForm({'field': ',,,,'})
       
  1437 >>> f.is_valid()
       
  1438 True
       
  1439 >>> f.cleaned_data
       
  1440 {'field': u',,,,'}
       
  1441 >>> f = CommaSeparatedIntegerForm({'field': '1.2'})
       
  1442 >>> f.errors
       
  1443 {'field': [u'Enter only digits separated by commas.']}
       
  1444 >>> f = CommaSeparatedIntegerForm({'field': '1,a,2'})
       
  1445 >>> f.errors
       
  1446 {'field': [u'Enter only digits separated by commas.']}
       
  1447 >>> f = CommaSeparatedIntegerForm({'field': '1,,2'})
       
  1448 >>> f.is_valid()
       
  1449 True
       
  1450 >>> f.cleaned_data
       
  1451 {'field': u'1,,2'}
       
  1452 >>> f = CommaSeparatedIntegerForm({'field': '1'})
       
  1453 >>> f.is_valid()
       
  1454 True
       
  1455 >>> f.cleaned_data
       
  1456 {'field': u'1'}
       
  1457 
       
  1458 This Price instance generated by this form is not valid because the quantity
       
  1459 field is required, but the form is valid because the field is excluded from
       
  1460 the form. This is for backwards compatibility.
       
  1461 
       
  1462 >>> class PriceForm(ModelForm):
       
  1463 ...     class Meta:
       
  1464 ...         model = Price
       
  1465 ...         exclude = ('quantity',)
       
  1466 >>> form = PriceForm({'price': '6.00'})
       
  1467 >>> form.is_valid()
       
  1468 True
       
  1469 >>> price = form.save(commit=False)
       
  1470 >>> price.full_clean()
       
  1471 Traceback (most recent call last):
       
  1472   ...
       
  1473 ValidationError: {'quantity': [u'This field cannot be null.']}
       
  1474 
       
  1475 The form should not validate fields that it doesn't contain even if they are
       
  1476 specified using 'fields', not 'exclude'.
       
  1477 ...     class Meta:
       
  1478 ...         model = Price
       
  1479 ...         fields = ('price',)
       
  1480 >>> form = PriceForm({'price': '6.00'})
       
  1481 >>> form.is_valid()
       
  1482 True
       
  1483 
       
  1484 The form should still have an instance of a model that is not complete and
       
  1485 not saved into a DB yet.
       
  1486 
       
  1487 >>> form.instance.price
       
  1488 Decimal('6.00')
       
  1489 >>> form.instance.quantity is None
       
  1490 True
       
  1491 >>> form.instance.pk is None
       
  1492 True
       
  1493 
       
  1494 # Choices on CharField and IntegerField
       
  1495 >>> class ArticleForm(ModelForm):
       
  1496 ...     class Meta:
       
  1497 ...         model = Article
       
  1498 >>> f = ArticleForm()
       
  1499 >>> f.fields['status'].clean('42')
       
  1500 Traceback (most recent call last):
       
  1501 ...
       
  1502 ValidationError: [u'Select a valid choice. 42 is not one of the available choices.']
       
  1503 
       
  1504 >>> class ArticleStatusForm(ModelForm):
       
  1505 ...     class Meta:
       
  1506 ...         model = ArticleStatus
       
  1507 >>> f = ArticleStatusForm()
       
  1508 >>> f.fields['status'].clean('z')
       
  1509 Traceback (most recent call last):
       
  1510 ...
       
  1511 ValidationError: [u'Select a valid choice. z is not one of the available choices.']
       
  1512 
       
  1513 # Foreign keys which use to_field #############################################
       
  1514 
       
  1515 >>> apple = Inventory.objects.create(barcode=86, name='Apple')
       
  1516 >>> pear = Inventory.objects.create(barcode=22, name='Pear')
       
  1517 >>> core = Inventory.objects.create(barcode=87, name='Core', parent=apple)
       
  1518 
       
  1519 >>> field = ModelChoiceField(Inventory.objects.all(), to_field_name='barcode')
       
  1520 >>> for choice in field.choices:
       
  1521 ...     print choice
       
  1522 (u'', u'---------')
       
  1523 (86, u'Apple')
       
  1524 (22, u'Pear')
       
  1525 (87, u'Core')
       
  1526 
       
  1527 >>> class InventoryForm(ModelForm):
       
  1528 ...     class Meta:
       
  1529 ...         model = Inventory
       
  1530 >>> form = InventoryForm(instance=core)
       
  1531 >>> print form['parent']
       
  1532 <select name="parent" id="id_parent">
       
  1533 <option value="">---------</option>
       
  1534 <option value="86" selected="selected">Apple</option>
       
  1535 <option value="22">Pear</option>
       
  1536 <option value="87">Core</option>
       
  1537 </select>
       
  1538 
       
  1539 >>> data = model_to_dict(core)
       
  1540 >>> data['parent'] = '22'
       
  1541 >>> form = InventoryForm(data=data, instance=core)
       
  1542 >>> core = form.save()
       
  1543 >>> core.parent
       
  1544 <Inventory: Pear>
       
  1545 
       
  1546 >>> class CategoryForm(ModelForm):
       
  1547 ...     description = forms.CharField()
       
  1548 ...     class Meta:
       
  1549 ...         model = Category
       
  1550 ...         fields = ['description', 'url']
       
  1551 
       
  1552 >>> CategoryForm.base_fields.keys()
       
  1553 ['description', 'url']
       
  1554 
       
  1555 >>> print CategoryForm()
       
  1556 <tr><th><label for="id_description">Description:</label></th><td><input type="text" name="description" id="id_description" /></td></tr>
       
  1557 <tr><th><label for="id_url">The URL:</label></th><td><input id="id_url" type="text" name="url" maxlength="40" /></td></tr>
       
  1558 
       
  1559 # Model field that returns None to exclude itself with explicit fields ########
       
  1560 
       
  1561 >>> class CustomFieldForExclusionForm(ModelForm):
       
  1562 ...     class Meta:
       
  1563 ...         model = CustomFieldForExclusionModel
       
  1564 ...         fields = ['name', 'markup']
       
  1565 
       
  1566 >>> CustomFieldForExclusionForm.base_fields.keys()
       
  1567 ['name']
       
  1568 
       
  1569 >>> print CustomFieldForExclusionForm()
       
  1570 <tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="10" /></td></tr>
       
  1571 
       
  1572 # Clean up
       
  1573 >>> import shutil
       
  1574 >>> shutil.rmtree(temp_storage_dir)
       
  1575 """