Kaydet (Commit) 9face54b authored tarafından Joseph Kocherhans's avatar Joseph Kocherhans

Fixed #9284. Fixed #8813. BaseModelFormSet now calls ModelForm.save().

This is backwards-incompatible if you were doing things to 'initial' in BaseModelFormSet.__init__, or if you relied on the internal _total_form_count or _initial_form_count attributes of BaseFormSet. Those attributes are now public methods.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10190 bcc190cf-cafb-0310-a4f2-bffc1f526a37
üst 1e082c38
...@@ -40,39 +40,51 @@ class BaseFormSet(StrAndUnicode): ...@@ -40,39 +40,51 @@ class BaseFormSet(StrAndUnicode):
self.error_class = error_class self.error_class = error_class
self._errors = None self._errors = None
self._non_form_errors = None self._non_form_errors = None
# initialization is different depending on whether we recieved data, initial, or nothing
if data or files:
self.management_form = ManagementForm(data, auto_id=self.auto_id, prefix=self.prefix)
if self.management_form.is_valid():
self._total_form_count = self.management_form.cleaned_data[TOTAL_FORM_COUNT]
self._initial_form_count = self.management_form.cleaned_data[INITIAL_FORM_COUNT]
else:
raise ValidationError('ManagementForm data is missing or has been tampered with')
else:
if initial:
self._initial_form_count = len(initial)
if self._initial_form_count > self.max_num and self.max_num > 0:
self._initial_form_count = self.max_num
self._total_form_count = self._initial_form_count + self.extra
else:
self._initial_form_count = 0
self._total_form_count = self.extra
if self._total_form_count > self.max_num and self.max_num > 0:
self._total_form_count = self.max_num
initial = {TOTAL_FORM_COUNT: self._total_form_count,
INITIAL_FORM_COUNT: self._initial_form_count}
self.management_form = ManagementForm(initial=initial, auto_id=self.auto_id, prefix=self.prefix)
# construct the forms in the formset # construct the forms in the formset
self._construct_forms() self._construct_forms()
def __unicode__(self): def __unicode__(self):
return self.as_table() return self.as_table()
def _management_form(self):
"""Returns the ManagementForm instance for this FormSet."""
if self.data or self.files:
form = ManagementForm(self.data, auto_id=self.auto_id, prefix=self.prefix)
if not form.is_valid():
raise ValidationError('ManagementForm data is missing or has been tampered with')
else:
form = ManagementForm(auto_id=self.auto_id, prefix=self.prefix, initial={
TOTAL_FORM_COUNT: self.total_form_count(),
INITIAL_FORM_COUNT: self.initial_form_count()
})
return form
management_form = property(_management_form)
def total_form_count(self):
"""Returns the total number of forms in this FormSet."""
if self.data or self.files:
return self.management_form.cleaned_data[TOTAL_FORM_COUNT]
else:
total_forms = self.initial_form_count() + self.extra
if total_forms > self.max_num > 0:
total_forms = self.max_num
return total_forms
def initial_form_count(self):
"""Returns the number of forms that are required in this FormSet."""
if self.data or self.files:
return self.management_form.cleaned_data[INITIAL_FORM_COUNT]
else:
# Use the length of the inital data if it's there, 0 otherwise.
initial_forms = self.initial and len(self.initial) or 0
if initial_forms > self.max_num > 0:
initial_forms = self.max_num
return initial_forms
def _construct_forms(self): def _construct_forms(self):
# instantiate all the forms and put them in self.forms # instantiate all the forms and put them in self.forms
self.forms = [] self.forms = []
for i in xrange(self._total_form_count): for i in xrange(self.total_form_count()):
self.forms.append(self._construct_form(i)) self.forms.append(self._construct_form(i))
def _construct_form(self, i, **kwargs): def _construct_form(self, i, **kwargs):
...@@ -89,7 +101,7 @@ class BaseFormSet(StrAndUnicode): ...@@ -89,7 +101,7 @@ class BaseFormSet(StrAndUnicode):
except IndexError: except IndexError:
pass pass
# Allow extra forms to be empty. # Allow extra forms to be empty.
if i >= self._initial_form_count: if i >= self.initial_form_count():
defaults['empty_permitted'] = True defaults['empty_permitted'] = True
defaults.update(kwargs) defaults.update(kwargs)
form = self.form(**defaults) form = self.form(**defaults)
...@@ -97,13 +109,13 @@ class BaseFormSet(StrAndUnicode): ...@@ -97,13 +109,13 @@ class BaseFormSet(StrAndUnicode):
return form return form
def _get_initial_forms(self): def _get_initial_forms(self):
"""Return a list of all the intial forms in this formset.""" """Return a list of all the initial forms in this formset."""
return self.forms[:self._initial_form_count] return self.forms[:self.initial_form_count()]
initial_forms = property(_get_initial_forms) initial_forms = property(_get_initial_forms)
def _get_extra_forms(self): def _get_extra_forms(self):
"""Return a list of all the extra forms in this formset.""" """Return a list of all the extra forms in this formset."""
return self.forms[self._initial_form_count:] return self.forms[self.initial_form_count():]
extra_forms = property(_get_extra_forms) extra_forms = property(_get_extra_forms)
# Maybe this should just go away? # Maybe this should just go away?
...@@ -127,10 +139,10 @@ class BaseFormSet(StrAndUnicode): ...@@ -127,10 +139,10 @@ class BaseFormSet(StrAndUnicode):
# that have had their deletion widget set to True # that have had their deletion widget set to True
if not hasattr(self, '_deleted_form_indexes'): if not hasattr(self, '_deleted_form_indexes'):
self._deleted_form_indexes = [] self._deleted_form_indexes = []
for i in range(0, self._total_form_count): for i in range(0, self.total_form_count()):
form = self.forms[i] form = self.forms[i]
# if this is an extra form and hasn't changed, don't consider it # if this is an extra form and hasn't changed, don't consider it
if i >= self._initial_form_count and not form.has_changed(): if i >= self.initial_form_count() and not form.has_changed():
continue continue
if form.cleaned_data[DELETION_FIELD_NAME]: if form.cleaned_data[DELETION_FIELD_NAME]:
self._deleted_form_indexes.append(i) self._deleted_form_indexes.append(i)
...@@ -150,10 +162,10 @@ class BaseFormSet(StrAndUnicode): ...@@ -150,10 +162,10 @@ class BaseFormSet(StrAndUnicode):
# by the form data. # by the form data.
if not hasattr(self, '_ordering'): if not hasattr(self, '_ordering'):
self._ordering = [] self._ordering = []
for i in range(0, self._total_form_count): for i in range(0, self.total_form_count()):
form = self.forms[i] form = self.forms[i]
# if this is an extra form and hasn't changed, don't consider it # if this is an extra form and hasn't changed, don't consider it
if i >= self._initial_form_count and not form.has_changed(): if i >= self.initial_form_count() and not form.has_changed():
continue continue
# don't add data marked for deletion to self.ordered_data # don't add data marked for deletion to self.ordered_data
if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]: if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
...@@ -221,7 +233,7 @@ class BaseFormSet(StrAndUnicode): ...@@ -221,7 +233,7 @@ class BaseFormSet(StrAndUnicode):
self._errors = [] self._errors = []
if not self.is_bound: # Stop further processing. if not self.is_bound: # Stop further processing.
return return
for i in range(0, self._total_form_count): for i in range(0, self.total_form_count()):
form = self.forms[i] form = self.forms[i]
self._errors.append(form.errors) self._errors.append(form.errors)
# Give self.clean() a chance to do cross-form validation. # Give self.clean() a chance to do cross-form validation.
...@@ -243,7 +255,7 @@ class BaseFormSet(StrAndUnicode): ...@@ -243,7 +255,7 @@ class BaseFormSet(StrAndUnicode):
"""A hook for adding extra fields on to each form instance.""" """A hook for adding extra fields on to each form instance."""
if self.can_order: if self.can_order:
# Only pre-fill the ordering field for initial forms. # Only pre-fill the ordering field for initial forms.
if index < self._initial_form_count: if index < self.initial_form_count():
form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_(u'Order'), initial=index+1, required=False) form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_(u'Order'), initial=index+1, required=False)
else: else:
form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_(u'Order'), required=False) form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_(u'Order'), required=False)
......
...@@ -54,6 +54,10 @@ def save_instance(form, instance, fields=None, fail_message='saved', ...@@ -54,6 +54,10 @@ def save_instance(form, instance, fields=None, fail_message='saved',
# callable upload_to can use the values from other fields. # callable upload_to can use the values from other fields.
if isinstance(f, models.FileField): if isinstance(f, models.FileField):
file_field_list.append(f) file_field_list.append(f)
# OneToOneField doesn't allow assignment of None. Guard against that
# instead of allowing it and throwing an error.
if isinstance(f, models.OneToOneField) and cleaned_data[f.name] is None:
pass
else: else:
f.save_form_data(instance, cleaned_data[f.name]) f.save_form_data(instance, cleaned_data[f.name])
...@@ -266,7 +270,13 @@ class BaseModelForm(BaseForm): ...@@ -266,7 +270,13 @@ class BaseModelForm(BaseForm):
lookup_kwargs = {} lookup_kwargs = {}
for field_name in unique_check: for field_name in unique_check:
lookup_kwargs[field_name] = self.cleaned_data[field_name] lookup_value = self.cleaned_data[field_name]
# ModelChoiceField will return an object instance rather than
# a raw primary key value, so convert it to a pk value before
# using it in a lookup.
if isinstance(self.fields[field_name], ModelChoiceField):
lookup_value = lookup_value.pk
lookup_kwargs[field_name] = lookup_value
qs = self.instance.__class__._default_manager.filter(**lookup_kwargs) qs = self.instance.__class__._default_manager.filter(**lookup_kwargs)
...@@ -357,12 +367,17 @@ class BaseModelFormSet(BaseFormSet): ...@@ -357,12 +367,17 @@ class BaseModelFormSet(BaseFormSet):
queryset=None, **kwargs): queryset=None, **kwargs):
self.queryset = queryset self.queryset = queryset
defaults = {'data': data, 'files': files, 'auto_id': auto_id, 'prefix': prefix} defaults = {'data': data, 'files': files, 'auto_id': auto_id, 'prefix': prefix}
defaults['initial'] = [model_to_dict(obj) for obj in self.get_queryset()]
defaults.update(kwargs) defaults.update(kwargs)
super(BaseModelFormSet, self).__init__(**defaults) super(BaseModelFormSet, self).__init__(**defaults)
def initial_form_count(self):
"""Returns the number of forms that are required in this FormSet."""
if not (self.data or self.files):
return len(self.get_queryset())
return super(BaseModelFormSet, self).initial_form_count()
def _construct_form(self, i, **kwargs): def _construct_form(self, i, **kwargs):
if i < self._initial_form_count: if i < self.initial_form_count():
kwargs['instance'] = self.get_queryset()[i] kwargs['instance'] = self.get_queryset()[i]
return super(BaseModelFormSet, self)._construct_form(i, **kwargs) return super(BaseModelFormSet, self)._construct_form(i, **kwargs)
...@@ -380,11 +395,11 @@ class BaseModelFormSet(BaseFormSet): ...@@ -380,11 +395,11 @@ class BaseModelFormSet(BaseFormSet):
def save_new(self, form, commit=True): def save_new(self, form, commit=True):
"""Saves and returns a new model instance for the given form.""" """Saves and returns a new model instance for the given form."""
return save_instance(form, self.model(), exclude=[self._pk_field.name], commit=commit) return form.save(commit=commit)
def save_existing(self, form, instance, commit=True): def save_existing(self, form, instance, commit=True):
"""Saves and returns an existing model instance for the given form.""" """Saves and returns an existing model instance for the given form."""
return save_instance(form, instance, exclude=[self._pk_field.name], commit=commit) return form.save(commit=commit)
def save(self, commit=True): def save(self, commit=True):
"""Saves model instances for every form, adding and changing instances """Saves model instances for every form, adding and changing instances
...@@ -410,7 +425,7 @@ class BaseModelFormSet(BaseFormSet): ...@@ -410,7 +425,7 @@ class BaseModelFormSet(BaseFormSet):
existing_objects[obj.pk] = obj existing_objects[obj.pk] = obj
saved_instances = [] saved_instances = []
for form in self.initial_forms: for form in self.initial_forms:
obj = existing_objects[form.cleaned_data[self._pk_field.name]] obj = existing_objects[form.cleaned_data[self._pk_field.name].pk]
if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]: if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
self.deleted_objects.append(obj) self.deleted_objects.append(obj)
obj.delete() obj.delete()
...@@ -438,10 +453,23 @@ class BaseModelFormSet(BaseFormSet): ...@@ -438,10 +453,23 @@ class BaseModelFormSet(BaseFormSet):
def add_fields(self, form, index): def add_fields(self, form, index):
"""Add a hidden field for the object's primary key.""" """Add a hidden field for the object's primary key."""
from django.db.models import AutoField from django.db.models import AutoField, OneToOneField, ForeignKey
self._pk_field = pk = self.model._meta.pk self._pk_field = pk = self.model._meta.pk
if pk.auto_created or isinstance(pk, AutoField): # If a pk isn't editable, then it won't be on the form, so we need to
form.fields[self._pk_field.name] = IntegerField(required=False, widget=HiddenInput) # add it here so we can tell which object is which when we get the
# data back. Generally, pk.editable should be false, but for some
# reason, auto_created pk fields and AutoField's editable attribute is
# True, so check for that as well.
if (not pk.editable) or (pk.auto_created or isinstance(pk, AutoField)):
try:
pk_value = self.get_queryset()[index].pk
except IndexError:
pk_value = None
if isinstance(pk, OneToOneField) or isinstance(pk, ForeignKey):
qs = pk.rel.to._default_manager.get_query_set()
else:
qs = self.model._default_manager.get_query_set()
form.fields[self._pk_field.name] = ModelChoiceField(qs, initial=pk_value, required=False, widget=HiddenInput)
super(BaseModelFormSet, self).add_fields(form, index) super(BaseModelFormSet, self).add_fields(form, index)
def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.formfield(), def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.formfield(),
...@@ -477,11 +505,15 @@ class BaseInlineFormSet(BaseModelFormSet): ...@@ -477,11 +505,15 @@ class BaseInlineFormSet(BaseModelFormSet):
super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix, super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix,
queryset=qs) queryset=qs)
def _construct_forms(self): def initial_form_count(self):
if self.save_as_new: if self.save_as_new:
self._total_form_count = self._initial_form_count return 0
self._initial_form_count = 0 return super(BaseInlineFormSet, self).initial_form_count()
super(BaseInlineFormSet, self)._construct_forms()
def total_form_count(self):
if self.save_as_new:
return super(BaseInlineFormSet, self).initial_form_count()
return super(BaseInlineFormSet, self).total_form_count()
def _construct_form(self, i, **kwargs): def _construct_form(self, i, **kwargs):
form = super(BaseInlineFormSet, self)._construct_form(i, **kwargs) form = super(BaseInlineFormSet, self)._construct_form(i, **kwargs)
...@@ -498,14 +530,15 @@ class BaseInlineFormSet(BaseModelFormSet): ...@@ -498,14 +530,15 @@ class BaseInlineFormSet(BaseModelFormSet):
get_default_prefix = classmethod(get_default_prefix) get_default_prefix = classmethod(get_default_prefix)
def save_new(self, form, commit=True): def save_new(self, form, commit=True):
fk_attname = self.fk.get_attname() # Use commit=False so we can assign the parent key afterwards, then
kwargs = {fk_attname: self.instance.pk} # save the object.
new_obj = self.model(**kwargs) obj = form.save(commit=False)
if fk_attname == self._pk_field.attname or self._pk_field.auto_created: setattr(obj, self.fk.get_attname(), self.instance.pk)
exclude = [self._pk_field.name] obj.save()
else: # form.save_m2m() can be called via the formset later on if commit=False
exclude = [] if commit and hasattr(form, 'save_m2m'):
return save_instance(form, new_obj, exclude=exclude, commit=commit) form.save_m2m()
return obj
def add_fields(self, form, index): def add_fields(self, form, index):
super(BaseInlineFormSet, self).add_fields(form, index) super(BaseInlineFormSet, self).add_fields(form, index)
...@@ -620,8 +653,6 @@ class InlineForeignKeyField(Field): ...@@ -620,8 +653,6 @@ class InlineForeignKeyField(Field):
# ensure the we compare the values as equal types. # ensure the we compare the values as equal types.
if force_unicode(value) != force_unicode(self.parent_instance.pk): if force_unicode(value) != force_unicode(self.parent_instance.pk):
raise ValidationError(self.error_messages['invalid_choice']) raise ValidationError(self.error_messages['invalid_choice'])
if self.pk_field:
return self.parent_instance.pk
return self.parent_instance return self.parent_instance
class ModelChoiceIterator(object): class ModelChoiceIterator(object):
......
...@@ -133,6 +133,20 @@ It is used to keep track of how many form instances are being displayed. If ...@@ -133,6 +133,20 @@ It is used to keep track of how many form instances are being displayed. If
you are adding new forms via JavaScript, you should increment the count fields you are adding new forms via JavaScript, you should increment the count fields
in this form as well. in this form as well.
.. versionadded:: 1.1
``total_form_count`` and ``initial_form_count``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``BaseModelFormSet`` has a couple of methods that are closely related to the
``ManagementForm``, ``total_form_count`` and ``initial_form_count``.
``total_form_count`` returns the total number of forms in this formset.
``initial_form_count`` returns the number of forms in the formset that were
pre-filled, and is also used to determine how many forms are required. You
will probably never need to override either of these methods, so please be
sure you understand what they do before doing so.
Custom formset validation Custom formset validation
~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
......
import datetime import datetime
from django import forms from django import forms
from django.db import models from django.db import models
...@@ -27,7 +25,7 @@ class Book(models.Model): ...@@ -27,7 +25,7 @@ class Book(models.Model):
def __unicode__(self): def __unicode__(self):
return self.title return self.title
class BookWithCustomPK(models.Model): class BookWithCustomPK(models.Model):
my_pk = models.DecimalField(max_digits=5, decimal_places=0, primary_key=True) my_pk = models.DecimalField(max_digits=5, decimal_places=0, primary_key=True)
author = models.ForeignKey(Author) author = models.ForeignKey(Author)
...@@ -35,13 +33,13 @@ class BookWithCustomPK(models.Model): ...@@ -35,13 +33,13 @@ class BookWithCustomPK(models.Model):
def __unicode__(self): def __unicode__(self):
return u'%s: %s' % (self.my_pk, self.title) return u'%s: %s' % (self.my_pk, self.title)
class AlternateBook(Book): class AlternateBook(Book):
notes = models.CharField(max_length=100) notes = models.CharField(max_length=100)
def __unicode__(self): def __unicode__(self):
return u'%s - %s' % (self.title, self.notes) return u'%s - %s' % (self.title, self.notes)
class AuthorMeeting(models.Model): class AuthorMeeting(models.Model):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
authors = models.ManyToManyField(Author) authors = models.ManyToManyField(Author)
...@@ -68,7 +66,7 @@ class Owner(models.Model): ...@@ -68,7 +66,7 @@ class Owner(models.Model):
auto_id = models.AutoField(primary_key=True) auto_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
place = models.ForeignKey(Place) place = models.ForeignKey(Place)
def __unicode__(self): def __unicode__(self):
return "%s at %s" % (self.name, self.place) return "%s at %s" % (self.name, self.place)
...@@ -81,7 +79,7 @@ class Location(models.Model): ...@@ -81,7 +79,7 @@ class Location(models.Model):
class OwnerProfile(models.Model): class OwnerProfile(models.Model):
owner = models.OneToOneField(Owner, primary_key=True) owner = models.OneToOneField(Owner, primary_key=True)
age = models.PositiveIntegerField() age = models.PositiveIntegerField()
def __unicode__(self): def __unicode__(self):
return "%s is %d" % (self.owner.name, self.age) return "%s is %d" % (self.owner.name, self.age)
...@@ -114,17 +112,17 @@ class MexicanRestaurant(Restaurant): ...@@ -114,17 +112,17 @@ class MexicanRestaurant(Restaurant):
# using inlineformset_factory. # using inlineformset_factory.
class Repository(models.Model): class Repository(models.Model):
name = models.CharField(max_length=25) name = models.CharField(max_length=25)
def __unicode__(self): def __unicode__(self):
return self.name return self.name
class Revision(models.Model): class Revision(models.Model):
repository = models.ForeignKey(Repository) repository = models.ForeignKey(Repository)
revision = models.CharField(max_length=40) revision = models.CharField(max_length=40)
class Meta: class Meta:
unique_together = (("repository", "revision"),) unique_together = (("repository", "revision"),)
def __unicode__(self): def __unicode__(self):
return u"%s (%s)" % (self.revision, unicode(self.repository)) return u"%s (%s)" % (self.revision, unicode(self.repository))
...@@ -146,7 +144,21 @@ class Team(models.Model): ...@@ -146,7 +144,21 @@ class Team(models.Model):
class Player(models.Model): class Player(models.Model):
team = models.ForeignKey(Team, null=True) team = models.ForeignKey(Team, null=True)
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
def __unicode__(self):
return self.name
# Models for testing custom ModelForm save methods in formsets and inline formsets
class Poet(models.Model):
name = models.CharField(max_length=100)
def __unicode__(self):
return self.name
class Poem(models.Model):
poet = models.ForeignKey(Poet)
name = models.CharField(max_length=100)
def __unicode__(self): def __unicode__(self):
return self.name return self.name
...@@ -337,13 +349,44 @@ used. ...@@ -337,13 +349,44 @@ used.
>>> AuthorFormSet = modelformset_factory(Author, max_num=2) >>> AuthorFormSet = modelformset_factory(Author, max_num=2)
>>> formset = AuthorFormSet(queryset=qs) >>> formset = AuthorFormSet(queryset=qs)
>>> [sorted(x.items()) for x in formset.initial] >>> [x.name for x in formset.get_queryset()]
[[('id', 1), ('name', u'Charles Baudelaire')], [('id', 3), ('name', u'Paul Verlaine')]] [u'Charles Baudelaire', u'Paul Verlaine']
>>> AuthorFormSet = modelformset_factory(Author, max_num=3) >>> AuthorFormSet = modelformset_factory(Author, max_num=3)
>>> formset = AuthorFormSet(queryset=qs) >>> formset = AuthorFormSet(queryset=qs)
>>> [sorted(x.items()) for x in formset.initial] >>> [x.name for x in formset.get_queryset()]
[[('id', 1), ('name', u'Charles Baudelaire')], [('id', 3), ('name', u'Paul Verlaine')], [('id', 2), ('name', u'Walt Whitman')]] [u'Charles Baudelaire', u'Paul Verlaine', u'Walt Whitman']
# ModelForm with a custom save method in a formset ###########################
>>> class PoetForm(forms.ModelForm):
... def save(self, commit=True):
... # change the name to "Vladimir Mayakovsky" just to be a jerk.
... author = super(PoetForm, self).save(commit=False)
... author.name = u"Vladimir Mayakovsky"
... if commit:
... author.save()
... return author
>>> PoetFormSet = modelformset_factory(Poet, form=PoetForm)
>>> data = {
... 'form-TOTAL_FORMS': '3', # the number of forms rendered
... 'form-INITIAL_FORMS': '0', # the number of forms with initial data
... 'form-0-name': 'Walt Whitman',
... 'form-1-name': 'Charles Baudelaire',
... 'form-2-name': '',
... }
>>> qs = Poet.objects.all()
>>> formset = PoetFormSet(data=data, queryset=qs)
>>> formset.is_valid()
True
>>> formset.save()
[<Poet: Vladimir Mayakovsky>, <Poet: Vladimir Mayakovsky>]
# Model inheritance in model formsets ######################################## # Model inheritance in model formsets ########################################
...@@ -553,6 +596,36 @@ True ...@@ -553,6 +596,36 @@ True
[<AlternateBook: Flowers of Evil - English translation of Les Fleurs du Mal>] [<AlternateBook: Flowers of Evil - English translation of Les Fleurs du Mal>]
# ModelForm with a custom save method in an inline formset ###################
>>> class PoemForm(forms.ModelForm):
... def save(self, commit=True):
... # change the name to "Brooklyn Bridge" just to be a jerk.
... poem = super(PoemForm, self).save(commit=False)
... poem.name = u"Brooklyn Bridge"
... if commit:
... poem.save()
... return poem
>>> PoemFormSet = inlineformset_factory(Poet, Poem, form=PoemForm)
>>> data = {
... 'poem_set-TOTAL_FORMS': '3', # the number of forms rendered
... 'poem_set-INITIAL_FORMS': '0', # the number of forms with initial data
... 'poem_set-0-name': 'The Cloud in Trousers',
... 'poem_set-1-name': 'I',
... 'poem_set-2-name': '',
... }
>>> poet = Poet.objects.create(name='Vladimir Mayakovsky')
>>> formset = PoemFormSet(data=data, instance=poet)
>>> formset.is_valid()
True
>>> formset.save()
[<Poem: Brooklyn Bridge>, <Poem: Brooklyn Bridge>]
# Test a custom primary key ################################################### # Test a custom primary key ###################################################
We need to ensure that it is displayed We need to ensure that it is displayed
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment