""" 34. Generating HTML forms from models Django provides shortcuts for creating Form objects from a model class and a model instance. The function django.newforms.form_for_model() takes a model class and returns a Form that is tied to the model. This Form works just like any other Form, with one additional method: save(). The save() method creates an instance of the model and returns that newly created instance. It saves the instance to the database if save(commit=True), which is default. If you pass commit=False, then you'll get the object without committing the changes to the database. The function django.newforms.form_for_instance() takes a model instance and returns a Form that is tied to the instance. This form works just like any other Form, with one additional method: save(). The save() method updates the model instance. It also takes a commit=True parameter. The function django.newforms.save_instance() takes a bound form instance and a model instance and saves the form's clean_data into the instance. It also takes a commit=True parameter. """ from django.db import models class Category(models.Model): name = models.CharField(maxlength=20) url = models.CharField('The URL', maxlength=40) def __str__(self): return self.name class Writer(models.Model): name = models.CharField(maxlength=50) def __str__(self): return self.name class Article(models.Model): headline = models.CharField(maxlength=50) pub_date = models.DateField() writer = models.ForeignKey(Writer) categories = models.ManyToManyField(Category, blank=True) def __str__(self): return self.headline __test__ = {'API_TESTS': """ >>> from django.newforms import form_for_model, form_for_instance, save_instance, BaseForm, Form, CharField >>> import datetime >>> Category.objects.all() [] >>> CategoryForm = form_for_model(Category) >>> f = CategoryForm() >>> print f >>> print f.as_ul()
  • >>> print f['name'] >>> f = CategoryForm(auto_id=False) >>> print f.as_ul()
  • Name:
  • The URL:
  • >>> f = CategoryForm({'name': 'Entertainment', 'url': 'entertainment'}) >>> f.is_valid() True >>> f.clean_data {'url': u'entertainment', 'name': u'Entertainment'} >>> obj = f.save() >>> obj >>> Category.objects.all() [] >>> f = CategoryForm({'name': "It's a test", 'url': 'test'}) >>> f.is_valid() True >>> f.clean_data {'url': u'test', 'name': u"It's a test"} >>> obj = f.save() >>> obj >>> Category.objects.all() [, ] If you call save() with commit=False, then it will return an object that hasn't yet been saved to the database. In this case, it's up to you to call save() on the resulting model instance. >>> f = CategoryForm({'name': 'Third test', 'url': 'third'}) >>> f.is_valid() True >>> f.clean_data {'url': u'third', 'name': u'Third test'} >>> obj = f.save(commit=False) >>> obj >>> Category.objects.all() [, ] >>> obj.save() >>> Category.objects.all() [, , ] If you call save() with invalid data, you'll get a ValueError. >>> f = CategoryForm({'name': '', 'url': 'foo'}) >>> f.errors {'name': [u'This field is required.']} >>> f.clean_data Traceback (most recent call last): ... AttributeError: 'CategoryForm' object has no attribute 'clean_data' >>> f.save() Traceback (most recent call last): ... ValueError: The Category could not be created because the data didn't validate. >>> f = CategoryForm({'name': '', 'url': 'foo'}) >>> f.save() Traceback (most recent call last): ... ValueError: The Category could not be created because the data didn't validate. Create a couple of Writers. >>> w = Writer(name='Mike Royko') >>> w.save() >>> w = Writer(name='Bob Woodward') >>> w.save() ManyToManyFields are represented by a MultipleChoiceField, and ForeignKeys are represented by a ChoiceField. >>> ArticleForm = form_for_model(Article) >>> f = ArticleForm(auto_id=False) >>> print f Headline: Pub date: Writer: Categories: You can pass a custom Form class to form_for_model. Make sure it's a subclass of BaseForm, not Form. >>> class CustomForm(BaseForm): ... def say_hello(self): ... print 'hello' >>> CategoryForm = form_for_model(Category, form=CustomForm) >>> f = CategoryForm() >>> f.say_hello() hello Use form_for_instance to create a Form from a model instance. The difference between this Form and one created via form_for_model is that the object's current values are inserted as 'initial' data in each Field. >>> w = Writer.objects.get(name='Mike Royko') >>> RoykoForm = form_for_instance(w) >>> f = RoykoForm(auto_id=False) >>> print f Name: >>> art = Article(headline='Test article', pub_date=datetime.date(1988, 1, 4), writer=w) >>> art.save() >>> art.id 1 >>> TestArticleForm = form_for_instance(art) >>> f = TestArticleForm(auto_id=False) >>> print f.as_ul()
  • Headline:
  • Pub date:
  • Writer:
  • Categories:
  • >>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04', 'writer': u'1'}) >>> f.is_valid() True >>> new_art = f.save() >>> new_art.id 1 >>> new_art = Article.objects.get(id=1) >>> new_art.headline 'New headline' Add some categories and test the many-to-many form output. >>> new_art.categories.all() [] >>> new_art.categories.add(Category.objects.get(name='Entertainment')) >>> new_art.categories.all() [] >>> TestArticleForm = form_for_instance(new_art) >>> f = TestArticleForm(auto_id=False) >>> print f.as_ul()
  • Headline:
  • Pub date:
  • Writer:
  • Categories:
  • Here, we define a custom Form. Because it happens to have the same fields as the Category model, we can use save_instance() to apply its changes to an existing Category instance. >>> class ShortCategory(Form): ... name = CharField(max_length=5) ... url = CharField(max_length=3) >>> cat = Category.objects.get(name='Third test') >>> cat >>> cat.id 3 >>> sc = ShortCategory({'name': 'Third', 'url': '3rd'}) >>> save_instance(sc, cat) >>> Category.objects.get(id=3) """}