Add CSV widget mixin with conditional rendering

This commit is contained in:
Ryan P Kilby 2016-09-23 17:06:30 -04:00
parent 5e92ac6e8b
commit 807e921608
2 changed files with 47 additions and 8 deletions

View File

@ -11,7 +11,7 @@ from django.utils.encoding import force_str
from django.utils.translation import ugettext_lazy as _
from .utils import handle_timezone
from .widgets import RangeWidget, LookupTypeWidget, CSVWidget
from .widgets import RangeWidget, LookupTypeWidget, CSVWidget, BaseCSVWidget
class RangeField(forms.MultiValueField):
@ -129,7 +129,28 @@ class BaseCSVField(forms.Field):
pass
"""
widget = CSVWidget
base_widget_class = BaseCSVWidget
def __init__(self, *args, **kwargs):
widget = kwargs.get('widget') or self.widget
kwargs['widget'] = self._get_widget_class(widget)
super(BaseCSVField, self).__init__(*args, **kwargs)
def _get_widget_class(self, widget):
# passthrough, allows for override
if isinstance(widget, BaseCSVWidget) or (
isinstance(widget, type) and
issubclass(widget, BaseCSVWidget)):
return widget
# complain since we are unable to reconstruct widget instances
assert isinstance(widget, type), \
"'%s.widget' must be a widget class, not %s." \
% (self.__class__.__name__, repr(widget))
bases = (self.base_widget_class, widget, )
return type(str('CSV%s' % widget.__name__), bases, {})
def clean(self, value):
if value is None:
@ -138,6 +159,10 @@ class BaseCSVField(forms.Field):
class BaseRangeField(BaseCSVField):
# Force use of text input, as range must always have two inputs. A date
# input would only allow a user to input one value and would always fail.
widget = CSVWidget
default_error_messages = {
'invalid_values': _('Range query expects two values.')
}

View File

@ -138,12 +138,12 @@ class BooleanWidget(forms.Select):
}.get(value, None)
class CSVWidget(forms.TextInput):
class BaseCSVWidget(forms.Widget):
def _isiterable(self, value):
return isinstance(value, Iterable) and not isinstance(value, string_types)
def value_from_datadict(self, data, files, name):
value = super(CSVWidget, self).value_from_datadict(data, files, name)
value = super(BaseCSVWidget, self).value_from_datadict(data, files, name)
if value is not None:
if value == '': # empty value should parse as an empty list
@ -152,8 +152,22 @@ class CSVWidget(forms.TextInput):
return None
def render(self, name, value, attrs=None):
if self._isiterable(value):
value = [force_text(format_value(self, v)) for v in value]
value = ','.join(list(value))
if not self._isiterable(value):
value = [value]
return super(CSVWidget, self).render(name, value, attrs)
if len(value) <= 1:
# delegate to main widget (Select, etc...) if not multiple values
value = value[0] if value else value
return super(BaseCSVWidget, self).render(name, value, attrs)
# if we have multiple values, we need to force render as a text input
# (otherwise, the additional values are lost)
surrogate = forms.TextInput()
value = [force_text(format_value(surrogate, v)) for v in value]
value = ','.join(list(value))
return surrogate.render(name, value, attrs)
class CSVWidget(BaseCSVWidget, forms.TextInput):
pass