newforms: MultiValueField and MultiWidget

41 views
Skip to first unread message

canen

unread,
Jan 24, 2007, 8:36:57 PM1/24/07
to Django users
Hello All,

I am resending this -- seems it didn't reach the last time, sorry if it
turns up twice
------------

I've been messing around with the new MultiValueField and MultiWidget.
I don't know if the shortcoming is with me or with the implementation
of the field and widget. Let's say I want to create a field that
displays two select fields that are related, I'll use currency display
as an example. The resulting HTML should look like:

<select name="budget_0" id="id_budget">
<option value="USD">USD</option>
<option value="EU">EU</option>
</select>
<select name="budget_1" id="id_budget">
<option value="1000">1000</option>
<option value="2000">2000</option>
</select>

and returns a value like "USD 1000". The implementation I have so far
is:

# Field
class MoneyField(MultiValueField):
def __init__(self, currency=(), amount=(), required=True,
widget=None, label=None, initial=None):
fields = (ChoiceField(choices=currency),
ChoiceField(choices=amount))
super(MoneyField, self).__init__(fields, required, widget,
label, initial)

def compress(self, data_list):
if data_list:
return " ".join(i for i in data_list)
return None

# Widget
class MoneyWidget(MultiWidget):
def __init__(self, attrs=None, currency=(), amount=()):
widgets = (Select(attrs=attrs, choices=currency),
Select(attrs=attrs, choices=amount))
super(MoneyWidget, self).__init__(widgets, attrs)

def decompress(self, value):
if value:
return value.split(' ', 1)
return ['', '']

The problem is, when I use the field I have to repeat the choices for
both widget and the field, like so:

CURRENCY_CHOICES = [('USD', 'USD'), ('EU', 'EU')]
AMOUNT_CHOICES = [('1000', '1000'), ('2000', '2000')]

class MyForm(forms.Form):
budget = MoneyField(
label="Vacation budget (hotel amount only)",
currency=CURRENCY_CHOICES,
amount=AMOUNT_CHOICES,
required=False,
widget=MoneyWidget(currency=CURRENCY_CHOICES,
amount=AMOUNT_CHOICES)
)

Am I doing something wrong here are is it just a caveat with using
ChoiceField? Any guidance would be appreciated.

Cheers!

canen

unread,
Jan 25, 2007, 3:16:52 PM1/25/07
to Django users
Answering my own question:

The widget can be declared as part of the field, e.g.

# Field
class MoneyField(MultiValueField):
def __init__(self, currency=(), amount=(), required=True,
widget=None, label=None, initial=None):

widget = widget or MoneyWidget(currency=currency,
amount=amount)
.....

Or something like that.

Chris Brand

unread,
Jan 25, 2007, 5:41:09 PM1/25/07
to django...@googlegroups.com
I've got some float fields in my model that default to 0.0.
In my templates, it would be nice to use something like the "if" tag to
display something like "Not set". I do seem to be able to do this with
integer fields that default to zero.

Can I do this with a built-in tag ?

Chris


Justin Findlay

unread,
Feb 19, 2007, 3:18:42 PM2/19/07
to Django users
On Jan 25, 1:16 pm, "canen" <nesta.campb...@gmail.com> wrote:
> Answering my own question:
>
> The widget can be declared as part of the field, e.g.
>
> # Field
> class MoneyField(MultiValueField):
> def __init__(self, currency=(), amount=(), required=True,
> widget=None, label=None, initial=None):
> widget = widget or MoneyWidget(currency=currency,
> amount=amount)
> .....

Canen, do you have the time to work out a full example? After much
trying I'm still not able to repeat the example without having to use
the double tuple values.


Justin

Justin Findlay

unread,
Feb 19, 2007, 4:44:21 PM2/19/07
to Django users
On Feb 19, 1:18 pm, "Justin Findlay" <jfind...@gmail.com> wrote:
> Canen, do you have the time to work out a full example? After much
> trying I'm still not able to repeat the example without having to use
> the double tuple values.

Nevermind. I misunderstood what you meant by repeating choices. The
choices themselves are doubled if you want the value/content pair of
the <select> to have the same string, but the list of choices need
only be supplied to the widget and not the field if you define the
widget as part of the field as canen has already said.


Justin

sparky

unread,
Apr 19, 2007, 6:20:15 AM4/19/07
to Django users

On Feb 19, 10:18 pm, "Justin Findlay" <jfind...@gmail.com> wrote:
> On Jan 25, 1:16 pm, "canen" <nesta.campb...@gmail.com> wrote:
>
> > Answering my own question:

> Thanks for the example!

I created another, but it uses initial data, and an object
representing the info.

class Postal(object):
def __init__(self, line1='', line2='', line3='', code=''):
self.line1 = line1
self.line2 = line2
self.line3 = line3
self.code = code

class PostalWidget(django.newforms.widgets.MultiWidget):
def __init__(self, attrs=None):
widgets = (django.newforms.widgets.TextInput(),
django.newforms.widgets.TextInput(),
django.newforms.widgets.TextInput(),
django.newforms.widgets.TextInput())

super(PostalWidget, self).__init__(widgets, attrs=attrs)

def decompress(self, value):
if value:

return [value.line1, value.line2, value.line3, value.code]
return [None, None, None, None]


class PostalField(django.newforms.fields.MultiValueField):
widget = PostalWidget
def __init__(self, required=True, widget=None, label=None,
initial=None):
fields = (django.newforms.fields.CharField(),
django.newforms.fields.CharField(),
django.newforms.fields.CharField(),
django.newforms.fields.CharField())
super(PostalField, self).__init__(fields=fields,
widget=widget, label=label, initial=initial)

def compress(self, data_list):
if data_list:

return Postal(data_list[0], data_list[1], data_list[2],
data_list[3])
return None

class EnrolmentForm(django.newforms.Form):
name = django.newforms.fields.CharField(label='Name')
surname = django.newforms.fields.CharField(label='Surname')
postalField = PostalField()

data= {'name':'piet', 'surname':'plesier', 'postalField_0' : 'cs',
'postalField_1' : 'pretoria', 'postalField_2' :
'Hatfield' ,'postalField_3' : '0084'}
#initial= {'name':'piet', 'surname':'plesier', 'postalField' :
Postal(line1='pompies')}
initial= {'name':'piet', 'surname':'plesier'}

f = EnrolmentForm(initial=initial)
print f.as_p()
f = EnrolmentForm(data=data)
print f.is_valid()
print f.errors
print f.clean_data

Reply all
Reply to author
Forward
0 new messages