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.

Reply via email to