Also take form field into account when floppifying widgets. That way all specific floppyform widgets will be used for modelforms (like EmailInput, NumberInput, etc.).

This commit is contained in:
Gregor Müllegger 2013-05-22 22:16:03 +02:00
parent 984b6fb8d4
commit 77fa844184
2 changed files with 216 additions and 16 deletions

View file

@ -71,7 +71,7 @@ def _create_multiwidget(widget_class, copy_attributes=(), init_arguments=()):
def create_new_multiwidget(original):
multiwidget = create_new_widget(original)
multiwidget.widgets = [
get_floppyform_widget(widget)
floppify_widget(widget)
for widget in multiwidget.widgets]
return multiwidget
return create_new_multiwidget
@ -155,8 +155,27 @@ _django_to_floppyforms_widget = {
init_arguments=('years', 'required')),
}
_django_field_to_floppyform_widget = {
django.forms.fields.FloatField:
_create_widget(floppyforms.widgets.NumberInput),
django.forms.fields.DecimalField:
_create_widget(floppyforms.widgets.NumberInput),
django.forms.fields.IntegerField:
_create_widget(floppyforms.widgets.NumberInput),
django.forms.fields.EmailField:
_create_widget(floppyforms.widgets.EmailInput),
django.forms.fields.URLField:
_create_widget(floppyforms.widgets.URLInput),
django.forms.fields.SlugField:
_create_widget(floppyforms.widgets.SlugInput),
django.forms.fields.IPAddressField:
_create_widget(floppyforms.widgets.IPAddressInput),
django.forms.fields.SplitDateTimeField:
_create_splitdatetimewidget(floppyforms.widgets.SplitDateTimeWidget),
}
def get_floppyform_widget(widget):
def floppify_widget(widget, field=None):
'''
Get an instance of django.forms.widgets.Widget and return a new widget
instance but using the corresponding floppyforms widget class.
@ -164,14 +183,41 @@ def get_floppyform_widget(widget):
Only original django widgets will be replaced with a floppyforms version.
The widget will be returned unaltered if it is not known, e.g. if it's a
custom widget from a third-party app.
The optional parameter ``field`` can be used to influence the widget
creation further. This is useful since floppyforms supports more widgets
than django does. For example is django using a ``TextInput`` for a
``EmailField``, but floppyforms has a better suiting widget called
``EmailInput``. If a widget is found specifically for the passed in
``field``, it will take precendence to the first parameter ``widget``
which will effectively be ignored.
'''
create_widget_class = _django_to_floppyforms_widget.get(
widget.__class__, None)
if create_widget_class is not None:
return create_widget_class(widget)
if field is not None:
create_widget = _django_field_to_floppyform_widget.get(
field.__class__)
if create_widget is not None:
# check if the default widget was replaced by a different one, in
# that case we cannot create the field specific floppyforms
# widget.
if field.widget.__class__ is field.__class__.widget:
return create_widget(widget)
create_widget = _django_to_floppyforms_widget.get(widget.__class__)
if create_widget is not None:
return create_widget(widget)
return widget
def floppify_form(form_class):
'''
Take a normal form and return a subclass of that form that replaces all
django widgets with the corresponding floppyforms widgets.
'''
new_form_class = type(form_class.__name__, (form_class,), {})
for field in new_form_class.base_fields.values():
field.widget = floppify_widget(field.widget, field=field)
return new_form_class
def modelform_factory(model, form=django.forms.models.ModelForm, fields=None,
exclude=None, formfield_callback=None, widgets=None):
form_class = django.forms.models.modelform_factory(
@ -181,6 +227,4 @@ def modelform_factory(model, form=django.forms.models.ModelForm, fields=None,
exclude=exclude,
formfield_callback=formfield_callback,
widgets=widgets)
for field in form_class.base_fields.values():
field.widget = get_floppyform_widget(field.widget)
return form_class
return floppify_form(form_class)

View file

@ -1,7 +1,7 @@
import floppyforms
from django import forms
from django.test import TestCase
from djadmin2.forms import get_floppyform_widget, modelform_factory
from djadmin2.forms import floppify_widget, floppify_form, modelform_factory
from ..models import Post
@ -16,7 +16,7 @@ class ModelFormFactoryTest(TestCase):
class GetFloppyformWidgetTest(TestCase):
def assertExpectWidget(self, instance, new_class_,
equal_attributes=None):
new_instance = get_floppyform_widget(instance)
new_instance = floppify_widget(instance)
self.assertEqual(new_instance.__class__, new_class_)
if equal_attributes:
for attribute in equal_attributes:
@ -38,7 +38,7 @@ class GetFloppyformWidgetTest(TestCase):
widget = forms.TextInput()
widget.is_required = True
widget.attrs = {'placeholder': 'Search ...'}
new_widget = get_floppyform_widget(widget)
new_widget = floppify_widget(widget)
self.assertFalse(widget.__dict__ is new_widget.__dict__)
new_widget.is_required = False
self.assertEqual(widget.is_required, True)
@ -177,7 +177,7 @@ class GetFloppyformWidgetTest(TestCase):
check_test = lambda v: False
widget = forms.widgets.CheckboxInput(check_test=check_test)
new_widget = get_floppyform_widget(widget)
new_widget = floppify_widget(widget)
self.assertEqual(widget.check_test, new_widget.check_test)
self.assertTrue(new_widget.check_test is check_test)
@ -235,7 +235,7 @@ class GetFloppyformWidgetTest(TestCase):
text_input = forms.widgets.TextInput()
widget = forms.widgets.MultiWidget([text_input])
new_widget = get_floppyform_widget(widget)
new_widget = floppify_widget(widget)
self.assertEqual(widget.widgets, new_widget.widgets)
self.assertTrue(new_widget.widgets[0] is text_input)
@ -247,7 +247,7 @@ class GetFloppyformWidgetTest(TestCase):
widget = forms.widgets.SplitDateTimeWidget(
date_format='DATE_FORMAT', time_format='TIME_FORMAT')
new_widget = get_floppyform_widget(widget)
new_widget = floppify_widget(widget)
self.assertTrue(isinstance(
new_widget.widgets[0], floppyforms.widgets.DateInput))
self.assertTrue(isinstance(
@ -263,7 +263,7 @@ class GetFloppyformWidgetTest(TestCase):
widget = forms.widgets.SplitHiddenDateTimeWidget(
date_format='DATE_FORMAT', time_format='TIME_FORMAT')
new_widget = get_floppyform_widget(widget)
new_widget = floppify_widget(widget)
self.assertTrue(isinstance(
new_widget.widgets[0], floppyforms.widgets.DateInput))
self.assertTrue(isinstance(
@ -286,3 +286,159 @@ class GetFloppyformWidgetTest(TestCase):
widget,
floppyforms.widgets.SelectDateWidget,
('attrs', 'years', 'required'))
class ModelFormTest(TestCase):
def test_custom_base_form(self):
class MyForm(forms.ModelForm):
pass
form_class = modelform_factory(model=Post, form=MyForm)
form = form_class()
self.assertTrue(isinstance(
form.fields['title'].widget,
floppyforms.widgets.TextInput))
def test_declared_fields(self):
class MyForm(forms.ModelForm):
subtitle = forms.CharField()
form_class = modelform_factory(model=Post, form=MyForm)
self.assertTrue(isinstance(
form_class.base_fields['subtitle'].widget,
floppyforms.widgets.TextInput))
self.assertTrue(isinstance(
form_class.declared_fields['subtitle'].widget,
floppyforms.widgets.TextInput))
self.assertTrue(isinstance(
form_class.base_fields['title'].widget,
floppyforms.widgets.TextInput))
# title is not defined in declared fields
def test_additional_form_fields(self):
class MyForm(forms.ModelForm):
subtitle = forms.CharField()
form_class = modelform_factory(model=Post, form=MyForm)
form = form_class()
self.assertTrue(isinstance(
form.fields['subtitle'].widget,
floppyforms.widgets.TextInput))
def test_subclassing_forms(self):
class MyForm(forms.ModelForm):
subtitle = forms.CharField()
class Meta:
model = Post
class ChildForm(MyForm):
created = forms.DateField()
form_class = modelform_factory(model=Post, form=ChildForm)
form = form_class()
self.assertTrue(isinstance(
form.fields['title'].widget,
floppyforms.widgets.TextInput))
self.assertTrue(isinstance(
form.fields['subtitle'].widget,
floppyforms.widgets.TextInput))
self.assertTrue(isinstance(
form.fields['created'].widget,
floppyforms.widgets.DateInput))
class FieldWidgetTest(TestCase):
def test_dont_overwrite_none_default_widget(self):
# we don't create the floppyform EmailInput for the email field here
# since we have overwritten the default widget. However we replace the
# django textarea with a floppyforms Textarea
email_input = forms.widgets.Textarea()
class MyForm(forms.ModelForm):
email = forms.EmailField(widget=email_input)
class Meta:
model = Post
form_class = floppify_form(MyForm)
widget = form_class().fields['email'].widget
self.assertFalse(isinstance(widget, floppyforms.widgets.EmailInput))
self.assertTrue(isinstance(widget, floppyforms.widgets.Textarea))
def test_float_field(self):
class MyForm(forms.ModelForm):
float = forms.FloatField()
form_class = modelform_factory(model=Post, form=MyForm)
widget = form_class().fields['float'].widget
self.assertTrue(isinstance(widget, floppyforms.widgets.NumberInput))
self.assertEqual(widget.input_type, 'number')
def test_decimal_field(self):
class MyForm(forms.ModelForm):
decimal = forms.DecimalField()
form_class = modelform_factory(model=Post, form=MyForm)
widget = form_class().fields['decimal'].widget
self.assertTrue(isinstance(widget, floppyforms.widgets.NumberInput))
self.assertEqual(widget.input_type, 'number')
def test_integer_field(self):
class MyForm(forms.ModelForm):
integer = forms.IntegerField()
form_class = modelform_factory(model=Post, form=MyForm)
widget = form_class().fields['integer'].widget
self.assertTrue(isinstance(widget, floppyforms.widgets.NumberInput))
self.assertEqual(widget.input_type, 'number')
def test_email_field(self):
class MyForm(forms.ModelForm):
email = forms.EmailField()
form_class = modelform_factory(model=Post, form=MyForm)
widget = form_class().fields['email'].widget
self.assertTrue(isinstance(widget, floppyforms.widgets.EmailInput))
self.assertEqual(widget.input_type, 'email')
def test_url_field(self):
class MyForm(forms.ModelForm):
url = forms.URLField()
form_class = modelform_factory(model=Post, form=MyForm)
widget = form_class().fields['url'].widget
self.assertTrue(isinstance(widget, floppyforms.widgets.URLInput))
self.assertEqual(widget.input_type, 'url')
def test_slug_field(self):
class MyForm(forms.ModelForm):
slug = forms.SlugField()
form_class = modelform_factory(model=Post, form=MyForm)
widget = form_class().fields['slug'].widget
self.assertTrue(isinstance(widget, floppyforms.widgets.SlugInput))
self.assertEqual(widget.input_type, 'text')
def test_ipaddress_field(self):
class MyForm(forms.ModelForm):
ipaddress = forms.IPAddressField()
form_class = modelform_factory(model=Post, form=MyForm)
widget = form_class().fields['ipaddress'].widget
self.assertTrue(isinstance(widget, floppyforms.widgets.IPAddressInput))
self.assertEqual(widget.input_type, 'text')
def test_splitdatetime_field(self):
class MyForm(forms.ModelForm):
splitdatetime = forms.SplitDateTimeField()
form_class = modelform_factory(model=Post, form=MyForm)
widget = form_class().fields['splitdatetime'].widget
self.assertTrue(isinstance(
widget, floppyforms.widgets.SplitDateTimeWidget))
self.assertTrue(isinstance(
widget.widgets[0], floppyforms.widgets.DateInput))
self.assertTrue(isinstance(
widget.widgets[1], floppyforms.widgets.TimeInput))