[Zope] How to make a ZPT-based form that calls itself? (Part II)

Tino Wildenhain tino at wildenhain.de
Thu May 19 13:40:43 EDT 2005


Am Donnerstag, den 19.05.2005, 11:56 -0400 schrieb Ken Winter:
> > -----Original Message-----
> > From: Lennart Regebro [mailto:regebro at gmail.com]
> > Sent: Thursday, May 19, 2005 11:07 AM
> > To: ken at sunward.org
> > Cc: J Cameron Cooper; zope at zope.org
> > Subject: Re: [Zope] How to make a ZPT-based form that calls itself? (Part
> > II)
> > 
> > On 5/19/05, Ken Winter <ken at sunward.org> wrote:
> > > * I assume you include data retrieved from (and updated in) an
> > underlying
> > > relational database.
> > >
> > > * Does "data" also include parameters (or arguments, or query string
> > > variables, or whatever you call them) passed from one HTML page to the
> > next?
> > 
> > Sure.
> > 
> > > This parameter-passing is what I seem to be having trouble with.  How
> > does
> > > that script get the info it needs from the previous page?  In my
> > example,
> > > when my "personform.htm" updates a record and then re-calls itself, how
> > does
> > > it get the person_id identifying the record it's supposed to display the
> > > second time around?
> > 
> > You put it in a hidden input-field.
> > 
> That's what I've been doing (I think), and it's not working.  Let me paste
> in a listing of the whole <div> tag that I'm using (with some irrelevant
> bits clipped out):
> 
> <div tal:repeat="person
> python:here.dbobs.read_person_by_id_py(context.REQUEST.person_id)">

the read_person_by_id_py can probably read the request variable
by its own. So no need to pass it like that.

> <table height="fit" width="fit" border="0" align="left" cellpadding="0"
> cellspacing="0">
>   <tr height="fit"> 
>     ...
> 		<td width="fit" align="left"> <em><strong> 
>             <font size="+2"><p align="left" tal:content="string:

Ugh! Make yourself familar with moden HTML and CSS, this saves a lot
of typing - and makes smaller templates.

> ${person/first_name} ${person/middle_names} ${person/last_name}">Name
> Filler</p></font>
>             </strong></em></td>
>   </tr>
> </table><br>
> <p>&nbsp;</p><br>
>         <form name="form1" id="form1" method="post"
> action="dbobs/update_person_py">
>           <table width="fit" border="0" cellspacing="0" cellpadding="0">
>   <tr height="fit">
>     <td width="fit" align="right"><em>&nbsp;&nbsp;First
> Name:&nbsp;</em></td>
>     <td width="fit"><input type="text" name="first_name" value=""
> tal:attributes="value string:${person/first_name}"/></td>

value person/first_name would be ok.

>   </tr>
>   <tr height="fit">
>     <td width="fit" align="right"><em>&nbsp;&nbsp;Middle
> Name(s):&nbsp;</em></td>
>     <td width="fit"><input type="text" name="middle_names" value=""
> tal:attributes="value string:${person/middle_names}"/></td>
>   </tr>
>   <tr height="fit">
>     <td width="fit" align="right"><em>&nbsp;&nbsp;Last Name:&nbsp;</em></td>
>     <td width="fit"><input type="text" name="last_name" value=""
> tal:attributes="value string:${person/last_name}"/></td>
>   </tr>
> </table>
> <input type="hidden" name="person_id" value="" tal:attributes="value
> string:${person/person_id}"/>
>           <p> 
>             <input type="submit" name="Submit" value="Save Changes" />
>             <input type="reset" name="Reset" value="Cancel Changes" />
>           </p>
>         </form>
> </div>
> 
> The tal:repeat in the first line is my attempt to grab all my data at the
> start.  The hidden input field at the bottom is my attempt to pass the
> person_id along when the page is trying to re-call itself.  The whole thing
> works OK when the page is called from elsewhere, passing the person_id in
> the form of a URL argument string along these lines:
> "personform.htm?person_id=35".  But it doesn't work when the Python script "
> update_person_py()" called by "personform.htm" itself tries to re-call
> "personform.htm", trusting the hidden field to convey the person_id data.
> Instead, the data update is performed but the next thing the browser shows
> is an AttributeError=person_id.  The body of the "update_person_py" script
> is:
> 
> id = context.REQUEST.get('person_id')
> if id:
>    container.update_person(\
>       person_id=id,\
>       first_name=context.REQUEST.get('first_name'),\
>       middle_names=context.REQUEST.get('middle_names'),\
>       last_name=context.REQUEST.get('last_name'))
> context.REQUEST.RESPONSE.redirect('personform.htm')

the redirect above when the script is included like 
you shown, causes the header: location: to be set.
Then Zope outputs all the HTML from ZPT and the
browser reads the location: header and makes a
new request with the URL found there. This 
new request obviously misses all the form information.


> What's wrong with this picture?

I'd use the controller-script approach here.
This is, you have the python script, which
checks the REQUEST for form information
and perhaps initialize some variables with
default data.

value=context.REQUEST.get('value',default) 
is the pattern here.

You would check for the name of the submit button
or some essential form data.

Depending on action, do your updates in database
or whatever.

Next read data out of database. Usually you connect
this to a preparation of data for the template. 
The pattern is like this:

somedata=[{'value1':r.value1,          # use comments a lot
           'value2':r.value2,          # possibly in every
           'value3':r.value3+r.value4  # line
           } for r in context.your_zsqlmethod(arg1=argument1)]

You can do a lot of preparation here.

next you decide which template you want to output
(can depend on request state or you use the same
template for all states)

return context.yourtemplate(data=somedata,value=value)

Where you use

tal:repeat="row options/data"

and tal:content="options/value"


So you can just use path expressions all over
the place since you prepare all the data in your
script. Complex data structures can be passed in
(nested) dictionaries.

HTH
Tino Wildenhain



More information about the Zope mailing list