From bcb7a31b2c6ca26906298725cc830bbfb0c4b383 Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Thu, 28 Dec 2006 02:34:53 +0000 Subject: [PATCH] newforms: Implemented apply_changes() method for form_for_instance Forms git-svn-id: http://code.djangoproject.com/svn/django/trunk@4253 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/newforms/models.py | 21 ++++++++++++-- tests/modeltests/model_forms/models.py | 39 +++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/django/newforms/models.py b/django/newforms/models.py index 12acc0ac83..e000e394e1 100644 --- a/django/newforms/models.py +++ b/django/newforms/models.py @@ -20,6 +20,22 @@ def create(self, save=True): obj.save() return obj +def make_apply_changes(opts, instance): + "Returns the apply_changes() method for a form_for_instance Form." + from django.db import models + def apply_changes(self, save=True): + if self.errors: + raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name) + clean_data = self.clean_data + for f in opts.fields + opts.many_to_many: + if isinstance(f, models.AutoField): + continue + setattr(instance, f.attname, clean_data[f.name]) + if save: + instance.save() + return instance + return apply_changes + def form_for_model(model, form=BaseForm): """ Returns a Form class for the given Django model class. @@ -45,12 +61,13 @@ def form_for_instance(instance, form=BaseForm): opts = model._meta field_list = [] for f in opts.fields + opts.many_to_many: - current_value = getattr(instance, f.attname) + current_value = f.value_from_object(instance) formfield = f.formfield(initial=current_value) if formfield: field_list.append((f.name, formfield)) fields = SortedDictFromList(field_list) - return type(opts.object_name + 'InstanceForm', (form,), {'fields': fields, '_model': model}) + return type(opts.object_name + 'InstanceForm', (form,), + {'fields': fields, '_model': model, 'apply_changes': make_apply_changes(opts, instance)}) def form_for_fields(field_list): "Returns a Form class for the given list of Django database field instances." diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py index 8ae9ddc6fb..60a3defde5 100644 --- a/tests/modeltests/model_forms/models.py +++ b/tests/modeltests/model_forms/models.py @@ -30,13 +30,14 @@ class Article(models.Model): headline = models.CharField(maxlength=50) pub_date = models.DateTimeField() writer = models.ForeignKey(Writer) - categories = models.ManyToManyField(Category) + 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, BaseForm +>>> import datetime >>> Category.objects.all() [] @@ -142,12 +143,42 @@ subclass of BaseForm, not Form. >>> 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. +Use form_for_instance to create a Form from a model instance. There are two +differences between this Form and one created via form_for_model. First, the +object's current values are inserted as 'initial' data in each Field. Second, +the Form gets an apply_changes() method instead of a create() method. >>> 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.apply_changes() +>>> new_art.id +1 +>>> new_art = Article.objects.get(id=1) +>>> new_art.headline +'New headline' """}