models.py 19 KB

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