My objective is to be able to hand-code my own view containing a form
that I can layout exactly as I want but retain the benefits of
accepts() re validation and db updating.

I found 2 obstacles with web2py out-of-the box:
1) In my view (I have less than 1 week of python), I did not have easy
access to the latest version of a field: defaults could be in
form.record.my_field (but not for a new record) but if the user has
input data then it is in form.vars.my_field.  I wanted a consistent
source and the only place is form.components but extracting the values
in the view seems difficult due to the nested nature of components.

2) For dropdown lists, the value is not enough.  I want the whole
<select><options> etc including which option is selected.

If I can achieve the above there are a couple of other requirements:
3) For completeness, support for the session/formkey mechanism and any
other hidden fields that may be specified.

4) If I display the whole control (as in 2) above), I don't want any
pre-formatted errors.  I can get the necessary info from form.errors.

With my solution, the usage is as follows:
- in a controller, after the SQLFORM(db.recipe) or FORM(whatever) is
created add form.customview=True

- in a view, after the <form> tag for your custom form add {{=form}} -
this will create a Storage called 'latest' in FORM with a 'value' and
a 'component' for each fieldname and return the following:
<!-- custom view -->
<input value="130911852535" type="hidden" name="_formkey" />
<input value="recipe" type="hidden" name="_formname" />
... plus any other hidden INPUTS specified in the form

- if you just want the latest value for a fieldname 'name' then code
something like
<input type="text" name="name" value="{{=form.latest.name['value']}}"/
>

- if you just want the whole control then code something like
{{=form.latest.name['component']}} to get
<input type="text" class="string" name="name" value="Cornish Pasty"
id="recipe_name" />"

- or, where the field has IS_IN_SET, code
{{=form.latest.type['component']}} to get
<select class="string" name="type" id="recipe_type"><option
value="A">accompaniment</option><option value="C">component</
option><option value="R" selected="selected">recipe</option></select>

So, assuming anyone's still interested, what changes did I make to the
web2py code?  Well, it's all in html.py and makes use of a new Storage
attribute called 'latest' added to FORM if customview is present and
True:

a) replace the existing FORM.xml() method with the following:
    def xml(self):
        if self.customview and self.customview==True:
            self._latestdata()
            # return hidden INPUTs in response to {{=form}}
            hidden=''
            field=self.latest['hidden']
            for component in field['component']:
                hidden+=component.xml()+'\n'
            return '<!-- custom view -->\n'+hidden
        else:
            # exactly the same as old xml() except hidden logic in
common method _hiddendata([])
            newform=FORM(*self.components,**self.attributes)
            self._hiddendata(newform.components)
            return DIV.xml(newform)
    # common method for creating/storing formkey and creating hidden
INPUTs - taken from original xml()
    def _hiddendata(self, components):
        if self.session!=None:
            _formkey='_formkey[%s]' % self.formname
            key=self.session[_formkey]=str(random.random())[2:]
 
components.append(INPUT(_type='hidden',_name='_formkey',_value=key))
        if self.formname!=None:
            components.append(INPUT(_type='hidden',_name='_formname',\
                           _value=self.formname))
        if self.attributes.has_key('hidden'):
            hidden=self['hidden']
            for key,value in hidden.items():
 
components.append(INPUT(_type='hidden',_name=key,_value=value))
    # create the Storage to hold value and component for each
fieldname + one for 'hidden' INPUTs
    def _latestdata(self):
        self.latest=Storage()
        components=[]
        self._hiddendata(components)
 
self.latest['hidden']={'value':'hidden','component':components}
        for component in self.components:
            component._latestdata(self.latest)

a) add the following to DIV to find the required component and store
in latest Storage
    def _latestdata(self, latest):
        # the following code checks whether the component can have
user input and
        # if true stores the value and component using the latestitem
helper
        # if false iterates through child components
        if self.attributes.has_key('_value') and\
           self.attributes.has_key('_name'):
 
latest[self.attributes['_name']]=self.latestitem(self.attributes['_value'],self)
        elif isinstance(self,SELECT) and\
           self.attributes.has_key('_name'):
 
latest[self.attributes['_name']]=self.latestitem(None,self)
        elif isinstance(self,TEXTAREA) and\
           self.attributes.has_key('value') and\
           self.attributes.has_key('_name'):
            value=self.attributes['value']
            if value==None: value=''
 
latest[self.attributes['_name']]=self.latestitem(value,self)
        elif self.components:
            for c in self.components:
                if hasattr(c,'_latestdata'): c._latestdata(latest)
    # store value and component ensuring any errors are ignored
    def latestitem(self, value, component):
        component.errors=None
        return {'value':value,'component':component}

That's it - it all seems to work a treat. All feedback gratefully
received (this is where Massimo tells me it could all have been done
with 2 lines of python and a regex) :-)

Bill
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"web2py Web Framework" group.
To post to this group, send email to web2py@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/web2py?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to