Skip to content

Commit

Permalink
[1.0.X] Fixed #10243, #11043 -- Corrected handling of formsets over a…
Browse files Browse the repository at this point in the history
… ForeignKey that uses to_field, and by extension, fixed the admin for handling fields of that type. Thanks to apollo13 for the initial patch.

Merge of r10756 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.0.X@10758 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
freakboy3742 committed May 13, 2009
1 parent 17d214a commit c482859
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 5 deletions.
23 changes: 18 additions & 5 deletions django/forms/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ def get_queryset(self):
#
# This logic is in the wrong place here on the 1.0.X branch.
# In the 1.1 series this logic exists as the QuerySet.ordered
# property, but since that's new in 1.1 here in 1.0 we just
# property, but since that's new in 1.1 here in 1.0 we just
# have to deal with this slightly smelly code here.
if qs.query.extra_order_by or qs.query.order_by:
ordered = True
Expand Down Expand Up @@ -675,7 +675,11 @@ def __init__(self, data=None, files=None, instance=None,
self.save_as_new = save_as_new
# 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()
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,
queryset=qs)

Expand Down Expand Up @@ -704,7 +708,7 @@ def get_default_prefix(cls):

def save_new(self, form, commit=True):
fk_attname = self.fk.get_attname()
kwargs = {fk_attname: self.instance.pk}
kwargs = {fk_attname: getattr(self.instance, self.fk.rel.field_name)}
new_obj = self.model(**kwargs)
if fk_attname == self._pk_field.attname or self._pk_field.auto_created:
exclude = [self._pk_field.name]
Expand All @@ -720,6 +724,7 @@ def add_fields(self, form, index):
# 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.
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))
)

Expand Down Expand Up @@ -816,8 +821,12 @@ class InlineForeignKeyField(Field):
def __init__(self, parent_instance, *args, **kwargs):
self.parent_instance = parent_instance
self.pk_field = kwargs.pop("pk_field", False)
self.to_field = kwargs.pop("to_field", None)
if self.parent_instance is not None:
kwargs["initial"] = self.parent_instance.pk
if self.to_field:
kwargs["initial"] = getattr(self.parent_instance, self.to_field)
else:
kwargs["initial"] = self.parent_instance.pk
kwargs["required"] = False
kwargs["widget"] = InlineForeignKeyHiddenInput
super(InlineForeignKeyField, self).__init__(*args, **kwargs)
Expand All @@ -829,7 +838,11 @@ def clean(self, value):
# if there is no value act as we did before.
return self.parent_instance
# 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'])
if self.pk_field:
return self.parent_instance.pk
Expand Down
Empty file.
9 changes: 9 additions & 0 deletions tests/regressiontests/model_formsets_regress/models.py
Original file line number Diff line number Diff line change
@@ -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()
24 changes: 24 additions & 0 deletions tests/regressiontests/model_formsets_regress/tests.py
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit c482859

Please sign in to comment.