PageRenderTime 376ms CodeModel.GetById 332ms app.highlight 37ms RepoModel.GetById 1ms app.codeStats 0ms

/tests/modeltests/model_forms/models.py

https://code.google.com/p/mango-py/
Python | 1613 lines | 1566 code | 30 blank | 17 comment | 4 complexity | 4b4e882b4960f284479369ffd12237dc MD5 | raw file

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file