Fixed #10243, #11043 -- Corrected handling of formsets over a ForeignKey that uses to_field, and by extension, fixed the admin for handling fields of that type. Thanks to apollo13 for the initial patch.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@10756 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
96b5b6b34c
commit
7215ffe8a4
|
@ -698,7 +698,11 @@ class BaseInlineFormSet(BaseModelFormSet):
|
||||||
self.save_as_new = save_as_new
|
self.save_as_new = save_as_new
|
||||||
# is there a better way to get the object descriptor?
|
# is there a better way to get the object descriptor?
|
||||||
self.rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name()
|
self.rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name()
|
||||||
qs = self.model._default_manager.filter(**{self.fk.name: self.instance})
|
if self.fk.rel.field_name == self.fk.rel.to._meta.pk.name:
|
||||||
|
backlink_value = self.instance
|
||||||
|
else:
|
||||||
|
backlink_value = getattr(self.instance, self.fk.rel.field_name)
|
||||||
|
qs = self.model._default_manager.filter(**{self.fk.name: backlink_value})
|
||||||
super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix,
|
super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix,
|
||||||
queryset=qs)
|
queryset=qs)
|
||||||
|
|
||||||
|
@ -733,7 +737,7 @@ class BaseInlineFormSet(BaseModelFormSet):
|
||||||
# Use commit=False so we can assign the parent key afterwards, then
|
# Use commit=False so we can assign the parent key afterwards, then
|
||||||
# save the object.
|
# save the object.
|
||||||
obj = form.save(commit=False)
|
obj = form.save(commit=False)
|
||||||
setattr(obj, self.fk.get_attname(), self.instance.pk)
|
setattr(obj, self.fk.get_attname(), getattr(self.instance, self.fk.rel.field_name))
|
||||||
if commit:
|
if commit:
|
||||||
obj.save()
|
obj.save()
|
||||||
# form.save_m2m() can be called via the formset later on if commit=False
|
# form.save_m2m() can be called via the formset later on if commit=False
|
||||||
|
@ -749,6 +753,7 @@ class BaseInlineFormSet(BaseModelFormSet):
|
||||||
# The foreign key field might not be on the form, so we poke at the
|
# The foreign key field might not be on the form, so we poke at the
|
||||||
# Model field to get the label, since we need that for error messages.
|
# Model field to get the label, since we need that for error messages.
|
||||||
form.fields[self.fk.name] = InlineForeignKeyField(self.instance,
|
form.fields[self.fk.name] = InlineForeignKeyField(self.instance,
|
||||||
|
to_field=self.fk.rel.field_name,
|
||||||
label=getattr(form.fields.get(self.fk.name), 'label', capfirst(self.fk.verbose_name))
|
label=getattr(form.fields.get(self.fk.name), 'label', capfirst(self.fk.verbose_name))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -845,7 +850,11 @@ class InlineForeignKeyField(Field):
|
||||||
def __init__(self, parent_instance, *args, **kwargs):
|
def __init__(self, parent_instance, *args, **kwargs):
|
||||||
self.parent_instance = parent_instance
|
self.parent_instance = parent_instance
|
||||||
self.pk_field = kwargs.pop("pk_field", False)
|
self.pk_field = kwargs.pop("pk_field", False)
|
||||||
|
self.to_field = kwargs.pop("to_field", None)
|
||||||
if self.parent_instance is not None:
|
if self.parent_instance is not None:
|
||||||
|
if self.to_field:
|
||||||
|
kwargs["initial"] = getattr(self.parent_instance, self.to_field)
|
||||||
|
else:
|
||||||
kwargs["initial"] = self.parent_instance.pk
|
kwargs["initial"] = self.parent_instance.pk
|
||||||
kwargs["required"] = False
|
kwargs["required"] = False
|
||||||
kwargs["widget"] = InlineForeignKeyHiddenInput
|
kwargs["widget"] = InlineForeignKeyHiddenInput
|
||||||
|
@ -858,7 +867,11 @@ class InlineForeignKeyField(Field):
|
||||||
# if there is no value act as we did before.
|
# if there is no value act as we did before.
|
||||||
return self.parent_instance
|
return self.parent_instance
|
||||||
# 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 self.to_field:
|
||||||
|
orig = getattr(self.parent_instance, self.to_field)
|
||||||
|
else:
|
||||||
|
orig = self.parent_instance.pk
|
||||||
|
if force_unicode(value) != force_unicode(orig):
|
||||||
raise ValidationError(self.error_messages['invalid_choice'])
|
raise ValidationError(self.error_messages['invalid_choice'])
|
||||||
return self.parent_instance
|
return self.parent_instance
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class User(models.Model):
|
||||||
|
username = models.CharField(max_length=12, unique=True)
|
||||||
|
serial = models.IntegerField()
|
||||||
|
|
||||||
|
class UserSite(models.Model):
|
||||||
|
user = models.ForeignKey(User, to_field="username")
|
||||||
|
data = models.IntegerField()
|
|
@ -0,0 +1,24 @@
|
||||||
|
from django.forms.models import inlineformset_factory
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from models import User, UserSite
|
||||||
|
|
||||||
|
class InlineFormsetTests(TestCase):
|
||||||
|
def test_formset_over_to_field(self):
|
||||||
|
"A formset over a ForeignKey with a to_field can be saved. Regression for #10243"
|
||||||
|
FormSet = inlineformset_factory(User, UserSite)
|
||||||
|
user = User.objects.create(serial=1, username='apollo13')
|
||||||
|
user.save()
|
||||||
|
data = {
|
||||||
|
'usersite_set-TOTAL_FORMS': u'1',
|
||||||
|
'usersite_set-INITIAL_FORMS': u'0',
|
||||||
|
'usersite_set-0-data': u'10',
|
||||||
|
'usersite_set-0-user': u'apollo13'
|
||||||
|
}
|
||||||
|
form_set = FormSet(data, instance=user)
|
||||||
|
if form_set.is_valid():
|
||||||
|
form_set.save()
|
||||||
|
usersite = UserSite.objects.all().values()[0]
|
||||||
|
self.assertEqual(usersite, {'data': 10, 'user_id': u'apollo13', 'id': 1})
|
||||||
|
else:
|
||||||
|
self.fail('Errors found on form:%s' % form_set.errors)
|
Loading…
Reference in New Issue