[Zope-DB] Hello!

Jim Penny jpenny@universal-fasteners.com
Mon, 21 Jul 2003 15:15:12 -0400


On Mon, 21 Jul 2003 19:52:53 +0200
Charlie Clark <charlie@begeistert.org> wrote:

> 
> On 2003-07-21 at 18:56:17 [+0200], Jim Penny wrote:
> > 1)  index_html is ALWAYS a Script (Python) in any of my code from
> > the last 18 months.
> > 
> > 2)  The main form will have ZPT in it, only to do error handling.
> > 
> > The ISBN_entry_form_pt  [the tal presents an error message, if
> > present, and fills in the form with the most recent values if said
> > values are in the request.  This should happen only after an
> > error.]:
> 
> Having looked at the forms and the scripts I can understand why you do
> this but it's not my preferred solution: I've gone on to breaking my
> scripts into smaller ones for manageability and explicit application
> function: my form processing script calls various dedicated checking
> scripts and usually has returns which are binary opposed: "if not
> error: success". I find the folder model also works well to help me
> remember without looking at the code what happens in any particular
> context. Mind you, I haven't got those 18 months yet!!! 

Look, my individual scripts are usually quite small, also.  Here is why
I do the this stuff the way I do it.  (And it is not the only way, and
not even the best way for many applications.)

1)  I am using zope purely as an application server.  The applications
have little to do with one another, usually are based on distinct
tables, etc.  So acquisition is not crucial for most of my code.  (It is
valuable, and I do use it.)

2)  index_html is ALWAYS about choosing what to present next.  It is
intrinsically about logic!  There are only two choices, DTML or
Python, and Python is by far the better choice.

3)  Standing it place.  This is the odd part, and the one that I have
seldom seen referenced elsewhere.  First, you do not want to have your
entire site in one folder!  This technique is only useful for an
application that has a manageable number of methods, say 50 to 200.
And name your methods carefully and systematically.  In this method, I
might have index_html, main_menu_pt, create_isbn_pt,
create_check_isbn_ps, insert_isbn_sql, find_isbn_pt, update_isbn_pt,
delete_isbn_pt, find_isbn_sql, update_isbn_sql, delete_isbn_sql, and
various _pt's and _sql's for reports.

Because the form of the call changes depending on the type of object
(some must have context and request, some may not have context, and some
normally have neither, I like to embed the type of the object in its
name)

4)  I typically collect all of the stuff dealing with one application in
a single folder (again this depends on complexity).  I deal with
index_html as a dispatch (event) loop server.  It has really, only one
purpose, to decide what to present next, and this is usually done on the
basis of a single variable, which I call next_state, by personal
convention, and maybe the other contents of the request.

5)  This means that index_html typically looks like:

if not request.has_key('next_state'):
  return container.main_menu_pt(context, request)
ns=request['next_state']
if ns=='Present Form 1":
  return container.form1_pt(context, request)
elif ns=='Present Form 2':
  return container.form2_pt(context, request)
...
elif ns='Process Form 1':
  err=container.check_form1_ps()
  if err:
    return container.form1_pt(context, request, error=err)
  else:
    # make any SQL calls, preserve state, whatever
    ....
    # then 
    return container.main_menu_pt(context, request)
...
else:
  return 'next_state value %s is not handled (yet)!' % next_state

It is a very regular structure, easy to parse, and easy to find which
methods are involved in parsing and handling a particular request.

6)  I want a single form to handle both the initial request and error
processing.  It is much easier to keep in synch.  It is WAY too easy to
edit and correct a form without doing the same for the corresponding
error form if they are separate objects.  Maintenance is harder than
initial writing, and more or less non-stop.  Users, once they get
reasonably quick, cheap, custom applications tend to keep wanting to
improve them.

7)  I do not like deeply foldered applications.  One of my first
applications involved a seven folder deep hierarchy, because I could not
see any way of processing the next form without using a folder.  It
proved very difficult to extend.  And when the process changed to a six
folder deep model, it was very unpleasant to correct.

8)  foldered applications often involve redirects.  Hate to use them, as
they are slow, and provide other technical problems.

So my typical data entry form looks like:

<div color:red tal:content="structure options/error|nothing"> 
 Present Error Message (if any) in red
</div>
<form action=. method=post>
  <input type=text name=name1 
     tal:attributes="value request/name1|nothing">  
  <input type=text name=name2
     tal:attributes="value request/name2|nothing">
  ...
  <div tal:omit-tag="">
    list all the things the user can do next,below
  </div>
  <input type=submit name="next_state" value="Process Form 1">
  ...
</form>

Note:  there are no variables in the sense that Charlie is talking about
being used here.   Charlie is thinking more about things that require a
calculation, careful lookup, or slow access.  Use variables for these. 
The internal variables in the form are purely pro forma, and separating
evaluation from use will actually make the form less legible, as you
will be constantly referring back to the top to make sure that the
variable is not "something special".  Put the "something specials" at
the top.  Leave the "pro formas" in the form.

The idea is that the form methods are dual use.  The same
form is used for both initial data entry and error presentation.  On
initial data entry, there will be no error in the options dictionary, so
you get an empty div at the top of the rendered form.  There will be no
form data, so each of the form variables will be filled with nothing.

On the other hand, if there is a structure named error in the options
dictionary, then it appears at the top of the form, in red, so the user
always finds errors in the same place, and with some highlighting.  (I
actually use a CSS that takes it to red small-caps.)  And, I use
check_..._ps to make validity checks and to transform the data into
canonical form (stripped, possibly capitalized, etc.), so if the user
input a value, the form will have that stuff already filled in for the
user to fix.  If the user did not enter a value, then nothing will be
entered.  Again, if the form is changed at some future date, I don't
have to worry about remembering to hunt down the error form an make the
analogous changes.  There is no distinct error form!

9)  Do not build your entire site using these ideas!  It will be way too
ugly and spaghetti-like.  Use a folder with this kind of arrangement to
implement a CRUD type application for a single database table, or set of
related tables.  Use folders for separate applications.

10)  Use breadcrumbs wherever possible.  You want to get your users out
of the urge to use the Back button.  The Back button may cause double
posting, and is almost always expensive.  You always have to present you
user with the possibility of getting to higher level folders without
much navigation.  With careful breadcrumbing and running in place, you
can design rich, featureful sites that never redirect, and for which
the user never has to touch the back button.

11)  SQL inserts, updates, and deletes should appear only in the
index_html.  They have nothing to present, and so should never appear in
a ZPT!

12)  SQL selects will be mostly used in a ZPT, in a tal:repeat loop. 
But there may be times when you have to drop out to a Script (Python). 
It depends purely on how complicated the logic is.  Pure presentation or
extremely simple logic, use a ZPT.  Complicated logic, or extensive
restucturing or complex sorts, use a script python.  [Here is an
exercise:  Suppose you have a set of button labels in table named
product_types, and another table items, that makes an (item,
product_type) association.  Given a particular item number, write
three-column set of radio buttons with labels coming from product_types
in ZPT.  The radio button of the product_type associated with this
particular item should be checked, and to make it easier to scan, should
be in strong font.  Do the same in Script Python.]

Jim Penny

Jim Penny


> 
> ZPT TALES and METAL-macros allow you to reduce the the form to a
> single ZPT and I initialise the variables all together at the top of
> the template as required. So I think this is somewhat a matter of
> style.
> 
> Charlie
> 
> _______________________________________________
> Zope-DB mailing list
> Zope-DB@zope.org
> http://mail.zope.org/mailman/listinfo/zope-db
> 
>