models.py 53 KB

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