From 55744e997f92f24608910eecf60509603ec87fec Mon Sep 17 00:00:00 2001 From: Brian Rosner Date: Tue, 15 Jul 2008 22:41:17 +0000 Subject: [PATCH] newforms-admin: Fixed #7230 -- Added a save_m2m method to BaseModelFormSet when commit=False is passed to save. Thanks Books Travis for the original report. git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@7930 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/newforms/models.py | 10 ++++++ docs/modelforms.txt | 4 ++- tests/modeltests/model_formsets/models.py | 44 +++++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/django/newforms/models.py b/django/newforms/models.py index 845ba975bb..43e2978ba8 100644 --- a/django/newforms/models.py +++ b/django/newforms/models.py @@ -331,6 +331,12 @@ class BaseModelFormSet(BaseFormSet): """Saves model instances for every form, adding and changing instances as necessary, and returns the list of instances. """ + if not commit: + self.saved_forms = [] + def save_m2m(): + for form in self.saved_forms: + form.save_m2m() + self.save_m2m = save_m2m return self.save_existing_objects(commit) + self.save_new_objects(commit) def save_existing_objects(self, commit=True): @@ -353,6 +359,8 @@ class BaseModelFormSet(BaseFormSet): if form.changed_data: self.changed_objects.append((obj, form.changed_data)) saved_instances.append(self.save_existing(form, obj, commit=commit)) + if not commit: + self.saved_forms.append(form) return saved_instances def save_new_objects(self, commit=True): @@ -365,6 +373,8 @@ class BaseModelFormSet(BaseFormSet): if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]: continue self.new_objects.append(self.save_new(form, commit=commit)) + if not commit: + self.saved_forms.append(form) return self.new_objects def add_fields(self, form, index): diff --git a/docs/modelforms.txt b/docs/modelforms.txt index 5cff11fdeb..9c06bc409d 100644 --- a/docs/modelforms.txt +++ b/docs/modelforms.txt @@ -454,7 +454,9 @@ model instances without any database interaction:: ... instance.save() This gives you the ability to attach data to the instances before saving them -to the database. +to the database. If your formset contains a ``ManyToManyField`` you will also +need to make a call to ``formset.save_m2m()`` to ensure the many-to-many +relationships are saved properly. Limiting the number of objects editable --------------------------------------- diff --git a/tests/modeltests/model_formsets/models.py b/tests/modeltests/model_formsets/models.py index 93519a4421..84265450fc 100644 --- a/tests/modeltests/model_formsets/models.py +++ b/tests/modeltests/model_formsets/models.py @@ -13,9 +13,19 @@ class Book(models.Model): def __unicode__(self): return self.title +class AuthorMeeting(models.Model): + name = models.CharField(max_length=100) + authors = models.ManyToManyField(Author) + created = models.DateField(editable=False) + + def __unicode__(self): + return self.name + __test__ = {'API_TESTS': """ +>>> from datetime import date + >>> from django.newforms.models import modelformset_factory >>> qs = Author.objects.all() @@ -162,6 +172,40 @@ True >>> formset.save() [] +Test the behavior of commit=False and save_m2m + +>>> meeting = AuthorMeeting.objects.create(created=date.today()) +>>> meeting.authors = Author.objects.all() + +# create an Author instance to add to the meeting. +>>> new_author = Author.objects.create(name=u'John Steinbeck') + +>>> AuthorMeetingFormSet = modelformset_factory(AuthorMeeting, extra=1, can_delete=True) +>>> data = { +... 'form-TOTAL_FORMS': '2', # the number of forms rendered +... 'form-INITIAL_FORMS': '1', # the number of forms with initial data +... 'form-MAX_FORMS': '0', # the max number of forms +... 'form-0-id': '1', +... 'form-0-name': '2nd Tuesday of the Week Meeting', +... 'form-0-authors': [2, 1, 3, 4], +... 'form-1-name': '', +... 'form-1-authors': '', +... 'form-1-DELETE': '', +... } +>>> formset = AuthorMeetingFormSet(data=data, queryset=AuthorMeeting.objects.all()) +>>> formset.is_valid() +True +>>> instances = formset.save(commit=False) +>>> for instance in instances: +... instance.created = date.today() +... instance.save() +>>> formset.save_m2m() +>>> instances[0].authors.all() +[, , , ] + +# delete the author we created to allow later tests to continue working. +>>> new_author.delete() + Test the behavior of max_num with model formsets. It should properly limit the queryset to reduce the amount of objects being pulled in when not being used.