Fixed #7888 -- Handle model inheritance with model formsets correctly. Thanks bpeschier for the report.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@8528 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
05e8c3ecd3
commit
82a1d5471c
|
@ -97,6 +97,7 @@ class Field(object):
|
||||||
self.help_text = help_text
|
self.help_text = help_text
|
||||||
self.db_column = db_column
|
self.db_column = db_column
|
||||||
self.db_tablespace = db_tablespace or settings.DEFAULT_INDEX_TABLESPACE
|
self.db_tablespace = db_tablespace or settings.DEFAULT_INDEX_TABLESPACE
|
||||||
|
self.auto_created = auto_created
|
||||||
|
|
||||||
# Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
|
# Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
|
||||||
self.db_index = db_index
|
self.db_index = db_index
|
||||||
|
|
|
@ -96,7 +96,7 @@ def model_to_dict(instance, fields=None, exclude=None):
|
||||||
the ``fields`` argument.
|
the ``fields`` argument.
|
||||||
"""
|
"""
|
||||||
# avoid a circular import
|
# avoid a circular import
|
||||||
from django.db.models.fields.related import ManyToManyField
|
from django.db.models.fields.related import ManyToManyField, OneToOneField
|
||||||
opts = instance._meta
|
opts = instance._meta
|
||||||
data = {}
|
data = {}
|
||||||
for f in opts.fields + opts.many_to_many:
|
for f in opts.fields + opts.many_to_many:
|
||||||
|
@ -115,6 +115,8 @@ def model_to_dict(instance, fields=None, exclude=None):
|
||||||
else:
|
else:
|
||||||
# MultipleChoiceWidget needs a list of pks, not object instances.
|
# MultipleChoiceWidget needs a list of pks, not object instances.
|
||||||
data[f.name] = [obj.pk for obj in f.value_from_object(instance)]
|
data[f.name] = [obj.pk for obj in f.value_from_object(instance)]
|
||||||
|
elif isinstance(f, OneToOneField):
|
||||||
|
data[f.attname] = f.value_from_object(instance)
|
||||||
else:
|
else:
|
||||||
data[f.name] = f.value_from_object(instance)
|
data[f.name] = f.value_from_object(instance)
|
||||||
return data
|
return data
|
||||||
|
@ -317,7 +319,7 @@ 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."""
|
||||||
if self.model._meta.has_auto_field:
|
if self.model._meta.pk.auto_created:
|
||||||
self._pk_field_name = self.model._meta.pk.attname
|
self._pk_field_name = self.model._meta.pk.attname
|
||||||
form.fields[self._pk_field_name] = IntegerField(required=False, widget=HiddenInput)
|
form.fields[self._pk_field_name] = IntegerField(required=False, widget=HiddenInput)
|
||||||
super(BaseModelFormSet, self).add_fields(form, index)
|
super(BaseModelFormSet, self).add_fields(form, index)
|
||||||
|
|
|
@ -13,6 +13,12 @@ import tempfile
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core.files.storage import FileSystemStorage
|
from django.core.files.storage import FileSystemStorage
|
||||||
|
|
||||||
|
# Python 2.3 doesn't have sorted()
|
||||||
|
try:
|
||||||
|
sorted
|
||||||
|
except NameError:
|
||||||
|
from django.utils.itercompat import sorted
|
||||||
|
|
||||||
temp_storage = FileSystemStorage(tempfile.gettempdir())
|
temp_storage = FileSystemStorage(tempfile.gettempdir())
|
||||||
|
|
||||||
ARTICLE_STATUS = (
|
ARTICLE_STATUS = (
|
||||||
|
@ -60,6 +66,9 @@ class ImprovedArticle(models.Model):
|
||||||
class ImprovedArticleWithParentLink(models.Model):
|
class ImprovedArticleWithParentLink(models.Model):
|
||||||
article = models.OneToOneField(Article, parent_link=True)
|
article = models.OneToOneField(Article, parent_link=True)
|
||||||
|
|
||||||
|
class BetterWriter(Writer):
|
||||||
|
pass
|
||||||
|
|
||||||
class PhoneNumber(models.Model):
|
class PhoneNumber(models.Model):
|
||||||
phone = models.PhoneNumberField()
|
phone = models.PhoneNumberField()
|
||||||
description = models.CharField(max_length=20)
|
description = models.CharField(max_length=20)
|
||||||
|
@ -91,7 +100,7 @@ class ImageFile(models.Model):
|
||||||
|
|
||||||
__test__ = {'API_TESTS': """
|
__test__ = {'API_TESTS': """
|
||||||
>>> from django import forms
|
>>> from django import forms
|
||||||
>>> from django.forms.models import ModelForm
|
>>> from django.forms.models import ModelForm, model_to_dict
|
||||||
>>> from django.core.files.uploadedfile import SimpleUploadedFile
|
>>> from django.core.files.uploadedfile import SimpleUploadedFile
|
||||||
|
|
||||||
The bare bones, absolutely nothing custom, basic case.
|
The bare bones, absolutely nothing custom, basic case.
|
||||||
|
@ -793,6 +802,11 @@ ValidationError: [u'Select a valid choice. 4 is not one of the available choices
|
||||||
>>> ImprovedArticleWithParentLinkForm.base_fields.keys()
|
>>> ImprovedArticleWithParentLinkForm.base_fields.keys()
|
||||||
[]
|
[]
|
||||||
|
|
||||||
|
>>> bw = BetterWriter(name=u'Joe Better')
|
||||||
|
>>> bw.save()
|
||||||
|
>>> sorted(model_to_dict(bw).keys())
|
||||||
|
['id', 'name', 'writer_ptr_id']
|
||||||
|
|
||||||
# PhoneNumberField ############################################################
|
# PhoneNumberField ############################################################
|
||||||
|
|
||||||
>>> class PhoneNumberForm(ModelForm):
|
>>> class PhoneNumberForm(ModelForm):
|
||||||
|
|
|
@ -14,6 +14,9 @@ class Author(models.Model):
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
class BetterAuthor(Author):
|
||||||
|
write_speed = models.IntegerField()
|
||||||
|
|
||||||
class Book(models.Model):
|
class Book(models.Model):
|
||||||
author = models.ForeignKey(Author)
|
author = models.ForeignKey(Author)
|
||||||
title = models.CharField(max_length=100)
|
title = models.CharField(max_length=100)
|
||||||
|
@ -229,6 +232,53 @@ used.
|
||||||
>>> [sorted(x.items()) for x in formset.initial]
|
>>> [sorted(x.items()) for x in formset.initial]
|
||||||
[[('id', 1), ('name', u'Charles Baudelaire')], [('id', 3), ('name', u'Paul Verlaine')], [('id', 2), ('name', u'Walt Whitman')]]
|
[[('id', 1), ('name', u'Charles Baudelaire')], [('id', 3), ('name', u'Paul Verlaine')], [('id', 2), ('name', u'Walt Whitman')]]
|
||||||
|
|
||||||
|
# Model inheritance in model formsets ########################################
|
||||||
|
|
||||||
|
>>> BetterAuthorFormSet = modelformset_factory(BetterAuthor)
|
||||||
|
>>> formset = BetterAuthorFormSet()
|
||||||
|
>>> for form in formset.forms:
|
||||||
|
... print form.as_p()
|
||||||
|
<p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" maxlength="100" /></p>
|
||||||
|
<p><label for="id_form-0-write_speed">Write speed:</label> <input type="text" name="form-0-write_speed" id="id_form-0-write_speed" /><input type="hidden" name="form-0-author_ptr_id" id="id_form-0-author_ptr_id" /></p>
|
||||||
|
|
||||||
|
>>> data = {
|
||||||
|
... 'form-TOTAL_FORMS': '1', # the number of forms rendered
|
||||||
|
... 'form-INITIAL_FORMS': '0', # the number of forms with initial data
|
||||||
|
... 'form-0-author_ptr_id': '',
|
||||||
|
... 'form-0-name': 'Ernest Hemingway',
|
||||||
|
... 'form-0-write_speed': '10',
|
||||||
|
... }
|
||||||
|
|
||||||
|
>>> formset = BetterAuthorFormSet(data)
|
||||||
|
>>> formset.is_valid()
|
||||||
|
True
|
||||||
|
>>> formset.save()
|
||||||
|
[<BetterAuthor: Ernest Hemingway>]
|
||||||
|
|
||||||
|
>>> formset = BetterAuthorFormSet()
|
||||||
|
>>> for form in formset.forms:
|
||||||
|
... print form.as_p()
|
||||||
|
<p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" value="Ernest Hemingway" maxlength="100" /></p>
|
||||||
|
<p><label for="id_form-0-write_speed">Write speed:</label> <input type="text" name="form-0-write_speed" value="10" id="id_form-0-write_speed" /><input type="hidden" name="form-0-author_ptr_id" value="4" id="id_form-0-author_ptr_id" /></p>
|
||||||
|
<p><label for="id_form-1-name">Name:</label> <input id="id_form-1-name" type="text" name="form-1-name" maxlength="100" /></p>
|
||||||
|
<p><label for="id_form-1-write_speed">Write speed:</label> <input type="text" name="form-1-write_speed" id="id_form-1-write_speed" /><input type="hidden" name="form-1-author_ptr_id" id="id_form-1-author_ptr_id" /></p>
|
||||||
|
|
||||||
|
>>> data = {
|
||||||
|
... 'form-TOTAL_FORMS': '2', # the number of forms rendered
|
||||||
|
... 'form-INITIAL_FORMS': '1', # the number of forms with initial data
|
||||||
|
... 'form-0-author_ptr_id': '4',
|
||||||
|
... 'form-0-name': 'Ernest Hemingway',
|
||||||
|
... 'form-0-write_speed': '10',
|
||||||
|
... 'form-1-author_ptr_id': '',
|
||||||
|
... 'form-1-name': '',
|
||||||
|
... 'form-1-write_speed': '',
|
||||||
|
... }
|
||||||
|
|
||||||
|
>>> formset = BetterAuthorFormSet(data)
|
||||||
|
>>> formset.is_valid()
|
||||||
|
True
|
||||||
|
>>> formset.save()
|
||||||
|
[]
|
||||||
|
|
||||||
# Inline Formsets ############################################################
|
# Inline Formsets ############################################################
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue