Muoto alkaa hahmottua - Form begins to take shape
I love the perfect geometry that spherical forms lend to a garden; the contrast of carved stone spheres set amongst a tangle of sprawling perennials, or the single blooms of Allium.
Maybe my strong feeling for the form is just an extension of my love for the human head, the sun, the moon and Uranus. Here is a collection of some of my favorite photographs featuring spherical forms:image 1: the simple form of osage orange (I wasn't feeling very creative)
Recently I had to implement a web form where the user was required to enter the date and time for a certain event. Many data types can conveniently be represented using several form fields to enhance usability. For instance if you want a form field representing the time, instead of requiring a specific time format to be entered, you can use a field for hours and a field for minutes. This is a very common thing to do. Using Django you can achieve this by implementing a custom MultiValueField and MultiWidget. The benefit of using a MultiValueField and MultiWidget to do this, is that you can refer to the fields as one field in django, and get a compressed data type back, like in this case a datetime.time type.
Since this is not yet documented, I am going to show how I implemented a time field with a select field for hours and a select field for minutes.
It looks like this:
MultiValueField
The responsibility of a MultiValueField is to take a list of field input data and compress them into a suitable return data type. Furthermore it validates each of the field input data.
Here is the custom MultiValueField I implemented for my time field:
class TimeSelectField(MultiValueField):
"""
Time multi field. Returns datetime.time object. Must be used together with TimeWidget
"""
def __init__(self, *args, **kwargs):
fields = (
forms.ChoiceField(choices=HOUR_CHOICES),
forms.ChoiceField(choices=MINUTE_CHOICES)
)
super(TimeSelectField, self).__init__(fields, *args, **kwargs) def compress(self, data_list):
if data_list:
return time(hour=int(data_list[0]), minute=int(data_list[1]))
return None
The compress method just returns a time object from the data that the widget (see below) has returned. The HOUR_CHOICES and MINUTE_CHOICES are lists of valid hour and minute values. We need to specify the fields used, so the MultiValueField can do basic validation of the input data. The TimeSelectField does not render two input fields, it only compresses and validates data. So we need to implement a TimeSelectWidget to use with it.
Here is the code for the TimeSelectWidget:
class TimeSelectWidget(MultiWidget):
"""
Time Widget, see TimeSelectField for info.
"""
def __init__(self, *args, **kwargs):
widgets = (
forms.Select(choices=HOUR_CHOICES),
forms.Select(choices=MINUTE_CHOICES)
)
super(TimeSelectWidget, self).__init__(widgets, *args, **kwargs) def decompress(self, value):
if value:
return [str(value.hour), str(value.minute)]
return [None, None] def format_output(self, rendered_widgets):
return u'\n'.join(rendered_widgets)
The TimeSelectWidget overrides two methods:
decompress does the opposite of compress. It takes a compressed data type (in this case a datetime.time) object and returns a data list. This is needed so we can provide an initial value to the widget.
format_output is used for rendering the widget. Since we have the individual widgets rendered, I just inserted a newline between the two.
We can now use the field as any other regular form field in Django, and do the following in the python shell:
>>> from datetime import time
>>> from django import newforms as forms
>>> from mymodule.forms import TimeSelectField, TimeSelectWidget
>>>
>>> t = time(hour=9, minute=0)
>>> class MyForm(forms.Form)
>>> time = TimeSelectField(widget=TimeSelectWidget, initial=t)
>>>
>>> f = MyForm()
>>> print f
<tr><th><label for="id_time_0">Time:</label></th><td><select name="time_0" id="id_time_0">
<option value="0">00</option>
<option value="1">01</option>
<option value="2">02</option>
<option value="3">03</option>
<option value="4">04</option>
<option value="5">05</option>
<option value="6">06</option>
<option value="7">07</option>
<option value="8">08</option>
<option value="9" selected="selected">09</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="14">14</option>
<option value="15">15</option>
<option value="16">16</option>
<option value="17">17</option>
<option value="18">18</option>
<option value="19">19</option>
<option value="20">20</option>
<option value="21">21</option>
<option value="22">22</option>
<option value="23">23</option>
</select>
<select name="time_1" id="id_time_1">
<option value="00">00</option>
<option value="15">15</option>
<option value="30">30</option>
<option value="45">45</option>
</select></td></tr>
Put in a short sentence to tell your visitors why you need the information
This form get it right. Example, refer to the Age and Postal Address field
300 g medisterpølse
5 dl makaroni
1 bk maiskorn
1 stk purre
1 stk rød paprika
4 stk egg
4 dl melk
1 ts salt
pepper
FREMGANGSMÅTE:Kok makaronien som anvist på pakken. Skjær opp purreløk, paprika og pølse, og legg dette sammen med makaroni og mais i en ildfast form. Visp sammen egg, melk, salt og pepper, og hell over pølseblandingen. Stekes i 225 grader i ca. 30 minutter.
1 stk rødløk
1 stk gulrot
1 Stilk stangselleri
1 fedd hvitløk
0,5 kg kjøttdeig
2 stk tomatbokser
1 ss sukker
5 ss Flytendekyllingbuljong, eller én buljongterning
1 dl rødvin
150 g ost
0,5 stk sitron, finrevet skall av (kun det gule)
4 dl melk
2 ss smør
2 ss hvetemel
300 g pastarør, eller lignende
salt og pepper
FREMGANGSMÅTE:Finhakk løk, gulrøtter og stangselleri. Ha dem i en stekepanne på middels varme med kjøttdeigen. Stek til løken får litt farge og ha i hvitløken og stek videre i ett minutt. Ha i hermetiske tomater, sukker, flytende kyllingkraft og rødvin. Kok videre i ca 10 minutter eller til det meste av væsken er fordampet. Smak til med salt og pepper Lag ostesaus
Smelt smør i en tykkbundet kjele. Ha i mel, rør godt og la det putre i ett minutt før du har i litt og litt av melken. La det koke opp mellom hver gang. Bland inn ost og sitronskall.
Kok pastaen i godt saltet vann og sil av. I en ildfast form, legg lagvis pasta, saus og kjøtt. (Det holder med to lag). Topp med litt ost før du setter den i ovnen i ca 10-15 minutter - eller til osten har smeltet.
Server gjerne med salat og bagetter.