Author: jbronn
Date: 2010-09-10 17:54:52 -0500 (Fri, 10 Sep 2010)
New Revision: 13731
Modified:
django/branches/releases/1.2.X/django/forms/models.py
django/branches/releases/1.2.X/tests/regressiontests/model_forms_regress/tests.py
django/branches/releases/1.2.X/tests/regressiontests/model_formsets_regress/tests.py
Log:
[1.2.X] Fixed #13095 -- `formfield_callback` keyword argument is now more sane
and works with widgets defined in `ModelForm.Meta.widgets`. Thanks, hvdklauw
for bug report, vung for initial patch, and carljm for review. Backport of
r13730 from trunk.
Modified: django/branches/releases/1.2.X/django/forms/models.py
===================================================================
--- django/branches/releases/1.2.X/django/forms/models.py 2010-09-10
22:46:22 UTC (rev 13730)
+++ django/branches/releases/1.2.X/django/forms/models.py 2010-09-10
22:54:52 UTC (rev 13731)
@@ -150,7 +150,7 @@
data[f.name] = f.value_from_object(instance)
return data
-def fields_for_model(model, fields=None, exclude=None, widgets=None,
formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)):
+def fields_for_model(model, fields=None, exclude=None, widgets=None,
formfield_callback=None):
"""
Returns a ``SortedDict`` containing form fields for the given model.
@@ -175,7 +175,14 @@
kwargs = {'widget': widgets[f.name]}
else:
kwargs = {}
- formfield = formfield_callback(f, **kwargs)
+
+ if formfield_callback is None:
+ formfield = f.formfield(**kwargs)
+ elif not callable(formfield_callback):
+ raise TypeError('formfield_callback must be a function or
callable')
+ else:
+ formfield = formfield_callback(f, **kwargs)
+
if formfield:
field_list.append((f.name, formfield))
else:
@@ -198,8 +205,7 @@
class ModelFormMetaclass(type):
def __new__(cls, name, bases, attrs):
- formfield_callback = attrs.pop('formfield_callback',
- lambda f, **kwargs: f.formfield(**kwargs))
+ formfield_callback = attrs.pop('formfield_callback', None)
try:
parents = [b for b in bases if issubclass(b, ModelForm)]
except NameError:
@@ -376,7 +382,7 @@
__metaclass__ = ModelFormMetaclass
def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
- formfield_callback=lambda f: f.formfield()):
+ formfield_callback=None):
# Create the inner Meta class. FIXME: ideally, we should be able to
# construct a ModelForm without creating and passing in a temporary
# inner class.
@@ -658,7 +664,7 @@
form.fields[self._pk_field.name] = ModelChoiceField(qs,
initial=pk_value, required=False, widget=HiddenInput)
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=None,
formset=BaseModelFormSet,
extra=1, can_delete=False, can_order=False,
max_num=None, fields=None, exclude=None):
@@ -813,7 +819,7 @@
formset=BaseInlineFormSet, fk_name=None,
fields=None, exclude=None,
extra=3, can_order=False, can_delete=True,
max_num=None,
- formfield_callback=lambda f: f.formfield()):
+ formfield_callback=None):
"""
Returns an ``InlineFormSet`` for the given kwargs.
Modified:
django/branches/releases/1.2.X/tests/regressiontests/model_forms_regress/tests.py
===================================================================
---
django/branches/releases/1.2.X/tests/regressiontests/model_forms_regress/tests.py
2010-09-10 22:46:22 UTC (rev 13730)
+++
django/branches/releases/1.2.X/tests/regressiontests/model_forms_regress/tests.py
2010-09-10 22:54:52 UTC (rev 13731)
@@ -250,3 +250,47 @@
form.is_valid()
# self.assertTrue(form.is_valid())
# self.assertEquals(form.cleaned_data['url'],
'http://example.com/test')
+
+
+class FormFieldCallbackTests(TestCase):
+
+ def test_baseform_with_widgets_in_meta(self):
+ """Regression for #13095: Using base forms with widgets defined in
Meta should not raise errors."""
+ widget = forms.Textarea()
+
+ class BaseForm(forms.ModelForm):
+ class Meta:
+ model = Person
+ widgets = {'name': widget}
+
+ Form = modelform_factory(Person, form=BaseForm)
+ self.assertTrue(Form.base_fields['name'].widget is widget)
+
+ def test_custom_callback(self):
+ """Test that a custom formfield_callback is used if provided"""
+
+ callback_args = []
+
+ def callback(db_field, **kwargs):
+ callback_args.append((db_field, kwargs))
+ return db_field.formfield(**kwargs)
+
+ widget = forms.Textarea()
+
+ class BaseForm(forms.ModelForm):
+ class Meta:
+ model = Person
+ widgets = {'name': widget}
+
+ _ = modelform_factory(Person, form=BaseForm,
+ formfield_callback=callback)
+ id_field, name_field = Person._meta.fields
+
+ self.assertEqual(callback_args,
+ [(id_field, {}), (name_field, {'widget': widget})])
+
+ def test_bad_callback(self):
+ # A bad callback provided by user still gives an error
+ self.assertRaises(TypeError, modelform_factory, Person,
+ formfield_callback='not a function or callable')
+
Modified:
django/branches/releases/1.2.X/tests/regressiontests/model_formsets_regress/tests.py
===================================================================
---
django/branches/releases/1.2.X/tests/regressiontests/model_formsets_regress/tests.py
2010-09-10 22:46:22 UTC (rev 13730)
+++
django/branches/releases/1.2.X/tests/regressiontests/model_formsets_regress/tests.py
2010-09-10 22:54:52 UTC (rev 13731)
@@ -1,8 +1,10 @@
-from django.forms.models import modelform_factory, inlineformset_factory
+from django import forms
+from django.forms.models import modelform_factory, inlineformset_factory,
modelformset_factory
from django.test import TestCase
from models import User, UserSite, Restaurant, Manager
+
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"
@@ -156,3 +158,61 @@
# you can create a formset with an instance of None
form = Form(instance=None)
formset = FormSet(instance=None)
+
+
+class CustomWidget(forms.CharField):
+ pass
+
+
+class UserSiteForm(forms.ModelForm):
+ class Meta:
+ model = UserSite
+ widgets = {'data': CustomWidget}
+
+
+class Callback(object):
+
+ def __init__(self):
+ self.log = []
+
+ def __call__(self, db_field, **kwargs):
+ self.log.append((db_field, kwargs))
+ return db_field.formfield(**kwargs)
+
+
+class FormfieldCallbackTests(TestCase):
+ """
+ Regression for #13095: Using base forms with widgets
+ defined in Meta should not raise errors.
+ """
+
+ def test_inlineformset_factory_default(self):
+ Formset = inlineformset_factory(User, UserSite, form=UserSiteForm)
+ form = Formset({}).forms[0]
+ self.assertTrue(isinstance(form['data'].field.widget, CustomWidget))
+
+ def test_modelformset_factory_default(self):
+ Formset = modelformset_factory(UserSite, form=UserSiteForm)
+ form = Formset({}).forms[0]
+ self.assertTrue(isinstance(form['data'].field.widget, CustomWidget))
+
+ def assertCallbackCalled(self, callback):
+ id_field, user_field, data_field = UserSite._meta.fields
+ expected_log = [
+ (id_field, {}),
+ (user_field, {}),
+ (data_field, {'widget': CustomWidget}),
+ ]
+ self.assertEqual(callback.log, expected_log)
+
+ def test_inlineformset_custom_callback(self):
+ callback = Callback()
+ inlineformset_factory(User, UserSite, form=UserSiteForm,
+ formfield_callback=callback)
+ self.assertCallbackCalled(callback)
+
+ def test_modelformset_custom_callback(self):
+ callback = Callback()
+ modelformset_factory(UserSite, form=UserSiteForm,
+ formfield_callback=callback)
+ self.assertCallbackCalled(callback)
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/django-updates?hl=en.