thirdparty/google_appengine/lib/django/tests/modeltests/model_forms/models.py
changeset 2866 a04b1e4126c4
parent 2864 2e0b0af889be
child 2868 9f7f269383f7
equal deleted inserted replaced
2864:2e0b0af889be 2866:a04b1e4126c4
     1 """
       
     2 36. Generating HTML forms from models
       
     3 
       
     4 Django provides shortcuts for creating Form objects from a model class and a
       
     5 model instance.
       
     6 
       
     7 The function django.newforms.form_for_model() takes a model class and returns
       
     8 a Form that is tied to the model. This Form works just like any other Form,
       
     9 with one additional method: save(). The save() method creates an instance
       
    10 of the model and returns that newly created instance. It saves the instance to
       
    11 the database if save(commit=True), which is default. If you pass
       
    12 commit=False, then you'll get the object without committing the changes to the
       
    13 database.
       
    14 
       
    15 The function django.newforms.form_for_instance() takes a model instance and
       
    16 returns a Form that is tied to the instance. This form works just like any
       
    17 other Form, with one additional method: save(). The save()
       
    18 method updates the model instance. It also takes a commit=True parameter.
       
    19 
       
    20 The function django.newforms.save_instance() takes a bound form instance and a
       
    21 model instance and saves the form's clean_data into the instance. It also takes
       
    22 a commit=True parameter.
       
    23 """
       
    24 
       
    25 from django.db import models
       
    26 
       
    27 class Category(models.Model):
       
    28     name = models.CharField(maxlength=20)
       
    29     url = models.CharField('The URL', maxlength=40)
       
    30 
       
    31     def __str__(self):
       
    32         return self.name
       
    33 
       
    34 class Writer(models.Model):
       
    35     name = models.CharField(maxlength=50, help_text='Use both first and last names.')
       
    36 
       
    37     def __str__(self):
       
    38         return self.name
       
    39 
       
    40 class Article(models.Model):
       
    41     headline = models.CharField(maxlength=50)
       
    42     pub_date = models.DateField()
       
    43     created = models.DateField(editable=False)
       
    44     writer = models.ForeignKey(Writer)
       
    45     article = models.TextField()
       
    46     categories = models.ManyToManyField(Category, blank=True)
       
    47 
       
    48     def save(self):
       
    49         import datetime
       
    50         if not self.id:
       
    51             self.created = datetime.date.today()
       
    52         return super(Article, self).save()
       
    53 
       
    54     def __str__(self):
       
    55         return self.headline
       
    56 
       
    57 class PhoneNumber(models.Model):
       
    58     phone = models.PhoneNumberField()
       
    59     description = models.CharField(maxlength=20)
       
    60 
       
    61     def __str__(self):
       
    62         return self.phone
       
    63 
       
    64 __test__ = {'API_TESTS': """
       
    65 >>> from django.newforms import form_for_model, form_for_instance, save_instance, BaseForm, Form, CharField
       
    66 >>> import datetime
       
    67 
       
    68 >>> Category.objects.all()
       
    69 []
       
    70 
       
    71 >>> CategoryForm = form_for_model(Category)
       
    72 >>> f = CategoryForm()
       
    73 >>> print f
       
    74 <tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr>
       
    75 <tr><th><label for="id_url">The URL:</label></th><td><input id="id_url" type="text" name="url" maxlength="40" /></td></tr>
       
    76 >>> print f.as_ul()
       
    77 <li><label for="id_name">Name:</label> <input id="id_name" type="text" name="name" maxlength="20" /></li>
       
    78 <li><label for="id_url">The URL:</label> <input id="id_url" type="text" name="url" maxlength="40" /></li>
       
    79 >>> print f['name']
       
    80 <input id="id_name" type="text" name="name" maxlength="20" />
       
    81 
       
    82 >>> f = CategoryForm(auto_id=False)
       
    83 >>> print f.as_ul()
       
    84 <li>Name: <input type="text" name="name" maxlength="20" /></li>
       
    85 <li>The URL: <input type="text" name="url" maxlength="40" /></li>
       
    86 
       
    87 >>> f = CategoryForm({'name': 'Entertainment', 'url': 'entertainment'})
       
    88 >>> f.is_valid()
       
    89 True
       
    90 >>> f.clean_data
       
    91 {'url': u'entertainment', 'name': u'Entertainment'}
       
    92 >>> obj = f.save()
       
    93 >>> obj
       
    94 <Category: Entertainment>
       
    95 >>> Category.objects.all()
       
    96 [<Category: Entertainment>]
       
    97 
       
    98 >>> f = CategoryForm({'name': "It's a test", 'url': 'test'})
       
    99 >>> f.is_valid()
       
   100 True
       
   101 >>> f.clean_data
       
   102 {'url': u'test', 'name': u"It's a test"}
       
   103 >>> obj = f.save()
       
   104 >>> obj
       
   105 <Category: It's a test>
       
   106 >>> Category.objects.all()
       
   107 [<Category: Entertainment>, <Category: It's a test>]
       
   108 
       
   109 If you call save() with commit=False, then it will return an object that
       
   110 hasn't yet been saved to the database. In this case, it's up to you to call
       
   111 save() on the resulting model instance.
       
   112 >>> f = CategoryForm({'name': 'Third test', 'url': 'third'})
       
   113 >>> f.is_valid()
       
   114 True
       
   115 >>> f.clean_data
       
   116 {'url': u'third', 'name': u'Third test'}
       
   117 >>> obj = f.save(commit=False)
       
   118 >>> obj
       
   119 <Category: Third test>
       
   120 >>> Category.objects.all()
       
   121 [<Category: Entertainment>, <Category: It's a test>]
       
   122 >>> obj.save()
       
   123 >>> Category.objects.all()
       
   124 [<Category: Entertainment>, <Category: It's a test>, <Category: Third test>]
       
   125 
       
   126 If you call save() with invalid data, you'll get a ValueError.
       
   127 >>> f = CategoryForm({'name': '', 'url': 'foo'})
       
   128 >>> f.errors
       
   129 {'name': [u'This field is required.']}
       
   130 >>> f.clean_data
       
   131 Traceback (most recent call last):
       
   132 ...
       
   133 AttributeError: 'CategoryForm' object has no attribute 'clean_data'
       
   134 >>> f.save()
       
   135 Traceback (most recent call last):
       
   136 ...
       
   137 ValueError: The Category could not be created because the data didn't validate.
       
   138 >>> f = CategoryForm({'name': '', 'url': 'foo'})
       
   139 >>> f.save()
       
   140 Traceback (most recent call last):
       
   141 ...
       
   142 ValueError: The Category could not be created because the data didn't validate.
       
   143 
       
   144 Create a couple of Writers.
       
   145 >>> w = Writer(name='Mike Royko')
       
   146 >>> w.save()
       
   147 >>> w = Writer(name='Bob Woodward')
       
   148 >>> w.save()
       
   149 
       
   150 ManyToManyFields are represented by a MultipleChoiceField, and ForeignKeys are
       
   151 represented by a ChoiceField.
       
   152 >>> ArticleForm = form_for_model(Article)
       
   153 >>> f = ArticleForm(auto_id=False)
       
   154 >>> print f
       
   155 <tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
       
   156 <tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr>
       
   157 <tr><th>Writer:</th><td><select name="writer">
       
   158 <option value="" selected="selected">---------</option>
       
   159 <option value="1">Mike Royko</option>
       
   160 <option value="2">Bob Woodward</option>
       
   161 </select></td></tr>
       
   162 <tr><th>Article:</th><td><textarea name="article"></textarea></td></tr>
       
   163 <tr><th>Categories:</th><td><select multiple="multiple" name="categories">
       
   164 <option value="1">Entertainment</option>
       
   165 <option value="2">It&#39;s a test</option>
       
   166 <option value="3">Third test</option>
       
   167 </select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr>
       
   168 
       
   169 You can pass a custom Form class to form_for_model. Make sure it's a
       
   170 subclass of BaseForm, not Form.
       
   171 >>> class CustomForm(BaseForm):
       
   172 ...     def say_hello(self):
       
   173 ...         print 'hello'
       
   174 >>> CategoryForm = form_for_model(Category, form=CustomForm)
       
   175 >>> f = CategoryForm()
       
   176 >>> f.say_hello()
       
   177 hello
       
   178 
       
   179 Use form_for_instance to create a Form from a model instance. The difference
       
   180 between this Form and one created via form_for_model is that the object's
       
   181 current values are inserted as 'initial' data in each Field.
       
   182 >>> w = Writer.objects.get(name='Mike Royko')
       
   183 >>> RoykoForm = form_for_instance(w)
       
   184 >>> f = RoykoForm(auto_id=False)
       
   185 >>> print f
       
   186 <tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br />Use both first and last names.</td></tr>
       
   187 
       
   188 >>> art = Article(headline='Test article', pub_date=datetime.date(1988, 1, 4), writer=w, article='Hello.')
       
   189 >>> art.save()
       
   190 >>> art.id
       
   191 1
       
   192 >>> TestArticleForm = form_for_instance(art)
       
   193 >>> f = TestArticleForm(auto_id=False)
       
   194 >>> print f.as_ul()
       
   195 <li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" /></li>
       
   196 <li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li>
       
   197 <li>Writer: <select name="writer">
       
   198 <option value="">---------</option>
       
   199 <option value="1" selected="selected">Mike Royko</option>
       
   200 <option value="2">Bob Woodward</option>
       
   201 </select></li>
       
   202 <li>Article: <textarea name="article">Hello.</textarea></li>
       
   203 <li>Categories: <select multiple="multiple" name="categories">
       
   204 <option value="1">Entertainment</option>
       
   205 <option value="2">It&#39;s a test</option>
       
   206 <option value="3">Third test</option>
       
   207 </select>  Hold down "Control", or "Command" on a Mac, to select more than one.</li>
       
   208 >>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04', 'writer': u'1', 'article': 'Hello.'})
       
   209 >>> f.is_valid()
       
   210 True
       
   211 >>> new_art = f.save()
       
   212 >>> new_art.id
       
   213 1
       
   214 >>> new_art = Article.objects.get(id=1)
       
   215 >>> new_art.headline
       
   216 'New headline'
       
   217 
       
   218 Add some categories and test the many-to-many form output.
       
   219 >>> new_art.categories.all()
       
   220 []
       
   221 >>> new_art.categories.add(Category.objects.get(name='Entertainment'))
       
   222 >>> new_art.categories.all()
       
   223 [<Category: Entertainment>]
       
   224 >>> TestArticleForm = form_for_instance(new_art)
       
   225 >>> f = TestArticleForm(auto_id=False)
       
   226 >>> print f.as_ul()
       
   227 <li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
       
   228 <li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li>
       
   229 <li>Writer: <select name="writer">
       
   230 <option value="">---------</option>
       
   231 <option value="1" selected="selected">Mike Royko</option>
       
   232 <option value="2">Bob Woodward</option>
       
   233 </select></li>
       
   234 <li>Article: <textarea name="article">Hello.</textarea></li>
       
   235 <li>Categories: <select multiple="multiple" name="categories">
       
   236 <option value="1" selected="selected">Entertainment</option>
       
   237 <option value="2">It&#39;s a test</option>
       
   238 <option value="3">Third test</option>
       
   239 </select>  Hold down "Control", or "Command" on a Mac, to select more than one.</li>
       
   240 
       
   241 >>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04',
       
   242 ...     'writer': u'1', 'article': u'Hello.', 'categories': [u'1', u'2']})
       
   243 >>> new_art = f.save()
       
   244 >>> new_art.id
       
   245 1
       
   246 >>> new_art = Article.objects.get(id=1)
       
   247 >>> new_art.categories.all()
       
   248 [<Category: Entertainment>, <Category: It's a test>]
       
   249 
       
   250 Now, submit form data with no categories. This deletes the existing categories.
       
   251 >>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04',
       
   252 ...     'writer': u'1', 'article': u'Hello.'})
       
   253 >>> new_art = f.save()
       
   254 >>> new_art.id
       
   255 1
       
   256 >>> new_art = Article.objects.get(id=1)
       
   257 >>> new_art.categories.all()
       
   258 []
       
   259 
       
   260 Create a new article, with categories, via the form.
       
   261 >>> ArticleForm = form_for_model(Article)
       
   262 >>> f = ArticleForm({'headline': u'The walrus was Paul', 'pub_date': u'1967-11-01',
       
   263 ...     'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
       
   264 >>> new_art = f.save()
       
   265 >>> new_art.id
       
   266 2
       
   267 >>> new_art = Article.objects.get(id=2)
       
   268 >>> new_art.categories.all()
       
   269 [<Category: Entertainment>, <Category: It's a test>]
       
   270 
       
   271 Create a new article, with no categories, via the form.
       
   272 >>> ArticleForm = form_for_model(Article)
       
   273 >>> f = ArticleForm({'headline': u'The walrus was Paul', 'pub_date': u'1967-11-01',
       
   274 ...     'writer': u'1', 'article': u'Test.'})
       
   275 >>> new_art = f.save()
       
   276 >>> new_art.id
       
   277 3
       
   278 >>> new_art = Article.objects.get(id=3)
       
   279 >>> new_art.categories.all()
       
   280 []
       
   281 
       
   282 Here, we define a custom Form. Because it happens to have the same fields as
       
   283 the Category model, we can use save_instance() to apply its changes to an
       
   284 existing Category instance.
       
   285 >>> class ShortCategory(Form):
       
   286 ...     name = CharField(max_length=5)
       
   287 ...     url = CharField(max_length=3)
       
   288 >>> cat = Category.objects.get(name='Third test')
       
   289 >>> cat
       
   290 <Category: Third test>
       
   291 >>> cat.id
       
   292 3
       
   293 >>> sc = ShortCategory({'name': 'Third', 'url': '3rd'})
       
   294 >>> save_instance(sc, cat)
       
   295 <Category: Third>
       
   296 >>> Category.objects.get(id=3)
       
   297 <Category: Third>
       
   298 
       
   299 Here, we demonstrate that choices for a ForeignKey ChoiceField are determined
       
   300 at runtime, based on the data in the database when the form is displayed, not
       
   301 the data in the database when the form is instantiated.
       
   302 >>> ArticleForm = form_for_model(Article)
       
   303 >>> f = ArticleForm(auto_id=False)
       
   304 >>> print f.as_ul()
       
   305 <li>Headline: <input type="text" name="headline" maxlength="50" /></li>
       
   306 <li>Pub date: <input type="text" name="pub_date" /></li>
       
   307 <li>Writer: <select name="writer">
       
   308 <option value="" selected="selected">---------</option>
       
   309 <option value="1">Mike Royko</option>
       
   310 <option value="2">Bob Woodward</option>
       
   311 </select></li>
       
   312 <li>Article: <textarea name="article"></textarea></li>
       
   313 <li>Categories: <select multiple="multiple" name="categories">
       
   314 <option value="1">Entertainment</option>
       
   315 <option value="2">It&#39;s a test</option>
       
   316 <option value="3">Third</option>
       
   317 </select>  Hold down "Control", or "Command" on a Mac, to select more than one.</li>
       
   318 >>> Category.objects.create(name='Fourth', url='4th')
       
   319 <Category: Fourth>
       
   320 >>> Writer.objects.create(name='Carl Bernstein')
       
   321 <Writer: Carl Bernstein>
       
   322 >>> print f.as_ul()
       
   323 <li>Headline: <input type="text" name="headline" maxlength="50" /></li>
       
   324 <li>Pub date: <input type="text" name="pub_date" /></li>
       
   325 <li>Writer: <select name="writer">
       
   326 <option value="" selected="selected">---------</option>
       
   327 <option value="1">Mike Royko</option>
       
   328 <option value="2">Bob Woodward</option>
       
   329 <option value="3">Carl Bernstein</option>
       
   330 </select></li>
       
   331 <li>Article: <textarea name="article"></textarea></li>
       
   332 <li>Categories: <select multiple="multiple" name="categories">
       
   333 <option value="1">Entertainment</option>
       
   334 <option value="2">It&#39;s a test</option>
       
   335 <option value="3">Third</option>
       
   336 <option value="4">Fourth</option>
       
   337 </select>  Hold down "Control", or "Command" on a Mac, to select more than one.</li>
       
   338 
       
   339 # ModelChoiceField ############################################################
       
   340 
       
   341 >>> from django.newforms import ModelChoiceField, ModelMultipleChoiceField
       
   342 
       
   343 >>> f = ModelChoiceField(Category.objects.all())
       
   344 >>> f.clean('')
       
   345 Traceback (most recent call last):
       
   346 ...
       
   347 ValidationError: [u'This field is required.']
       
   348 >>> f.clean(None)
       
   349 Traceback (most recent call last):
       
   350 ...
       
   351 ValidationError: [u'This field is required.']
       
   352 >>> f.clean(0)
       
   353 Traceback (most recent call last):
       
   354 ...
       
   355 ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
       
   356 >>> f.clean(3)
       
   357 <Category: Third>
       
   358 >>> f.clean(2)
       
   359 <Category: It's a test>
       
   360 
       
   361 # Add a Category object *after* the ModelChoiceField has already been
       
   362 # instantiated. This proves clean() checks the database during clean() rather
       
   363 # than caching it at time of instantiation.
       
   364 >>> Category.objects.create(name='Fifth', url='5th')
       
   365 <Category: Fifth>
       
   366 >>> f.clean(5)
       
   367 <Category: Fifth>
       
   368 
       
   369 # Delete a Category object *after* the ModelChoiceField has already been
       
   370 # instantiated. This proves clean() checks the database during clean() rather
       
   371 # than caching it at time of instantiation.
       
   372 >>> Category.objects.get(url='5th').delete()
       
   373 >>> f.clean(5)
       
   374 Traceback (most recent call last):
       
   375 ...
       
   376 ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
       
   377 
       
   378 >>> f = ModelChoiceField(Category.objects.filter(pk=1), required=False)
       
   379 >>> print f.clean('')
       
   380 None
       
   381 >>> f.clean('')
       
   382 >>> f.clean('1')
       
   383 <Category: Entertainment>
       
   384 >>> f.clean('100')
       
   385 Traceback (most recent call last):
       
   386 ...
       
   387 ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
       
   388 
       
   389 # ModelMultipleChoiceField ####################################################
       
   390 
       
   391 >>> f = ModelMultipleChoiceField(Category.objects.all())
       
   392 >>> f.clean(None)
       
   393 Traceback (most recent call last):
       
   394 ...
       
   395 ValidationError: [u'This field is required.']
       
   396 >>> f.clean([])
       
   397 Traceback (most recent call last):
       
   398 ...
       
   399 ValidationError: [u'This field is required.']
       
   400 >>> f.clean([1])
       
   401 [<Category: Entertainment>]
       
   402 >>> f.clean([2])
       
   403 [<Category: It's a test>]
       
   404 >>> f.clean(['1'])
       
   405 [<Category: Entertainment>]
       
   406 >>> f.clean(['1', '2'])
       
   407 [<Category: Entertainment>, <Category: It's a test>]
       
   408 >>> f.clean([1, '2'])
       
   409 [<Category: Entertainment>, <Category: It's a test>]
       
   410 >>> f.clean((1, '2'))
       
   411 [<Category: Entertainment>, <Category: It's a test>]
       
   412 >>> f.clean(['100'])
       
   413 Traceback (most recent call last):
       
   414 ...
       
   415 ValidationError: [u'Select a valid choice. 100 is not one of the available choices.']
       
   416 >>> f.clean('hello')
       
   417 Traceback (most recent call last):
       
   418 ...
       
   419 ValidationError: [u'Enter a list of values.']
       
   420 
       
   421 # Add a Category object *after* the ModelChoiceField has already been
       
   422 # instantiated. This proves clean() checks the database during clean() rather
       
   423 # than caching it at time of instantiation.
       
   424 >>> Category.objects.create(id=6, name='Sixth', url='6th')
       
   425 <Category: Sixth>
       
   426 >>> f.clean([6])
       
   427 [<Category: Sixth>]
       
   428 
       
   429 # Delete a Category object *after* the ModelChoiceField has already been
       
   430 # instantiated. This proves clean() checks the database during clean() rather
       
   431 # than caching it at time of instantiation.
       
   432 >>> Category.objects.get(url='6th').delete()
       
   433 >>> f.clean([6])
       
   434 Traceback (most recent call last):
       
   435 ...
       
   436 ValidationError: [u'Select a valid choice. 6 is not one of the available choices.']
       
   437 
       
   438 >>> f = ModelMultipleChoiceField(Category.objects.all(), required=False)
       
   439 >>> f.clean([])
       
   440 []
       
   441 >>> f.clean(())
       
   442 []
       
   443 >>> f.clean(['10'])
       
   444 Traceback (most recent call last):
       
   445 ...
       
   446 ValidationError: [u'Select a valid choice. 10 is not one of the available choices.']
       
   447 >>> f.clean(['3', '10'])
       
   448 Traceback (most recent call last):
       
   449 ...
       
   450 ValidationError: [u'Select a valid choice. 10 is not one of the available choices.']
       
   451 >>> f.clean(['1', '10'])
       
   452 Traceback (most recent call last):
       
   453 ...
       
   454 ValidationError: [u'Select a valid choice. 10 is not one of the available choices.']
       
   455 
       
   456 # PhoneNumberField ############################################################
       
   457 
       
   458 >>> PhoneNumberForm = form_for_model(PhoneNumber)
       
   459 >>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'})
       
   460 >>> f.is_valid()
       
   461 True
       
   462 >>> f.clean_data
       
   463 {'phone': u'312-555-1212', 'description': u'Assistance'}
       
   464 """}