Why is it so hard to do simple things?
I'm new to Zope, and new to Python, but certainly not new to software engineering, with most of my background in C, Perl and Java. I'm trying to do something quite simple. I'd like to see if a user has checked off a radio box. If it's their first time to the page, then the attribute request.perm_or_temp hasn't been set, and one of the boxes should be checked by default. If it's their 2nd time to the page (perhaps an error, and they're POSTing again) then I'd like to retain the state of the checkbox. If I was doing this in perl it'd be quite easy; I could check the query variable, and act on it -- but now I have to deal with things like not being able to even examine the variable without throwing an exception, and the fact that Python doesn't short-circuit blows. i.e. You can't examine something like: if (hasattr(request,'perm_or_temp') and request.perm_or_temp == 'P') ... In Zope, I assume that I should be using DTML here, and perhaps dtml-in over the items in the radio list. What's the proper way to do this zope? My current version is quite poor, takes up too many lines, and is disgustingly inefficient: (this is a dtml-method) # Default block -- this is a really shitty way to do this sort # of thing. I hate python. if not hasattr(request,'perm_or_temp'): print "<INPUT TYPE='RADIO' NAME=perm_or_temp VALUE='P' CHECKED> Permanent" print "<INPUT TYPE='RADIO' NAME=perm_or_temp VALUE='T'> Temporary" return printed # else we have it, process the request print "<INPUT TYPE='RADIO' NAME=perm_or_temp VALUE='P'>" if request.perm_or_temp == 'P'): print "CHECKED" print "> Permanent" print "<INPUT TYPE='RADIO' NAME=perm_or_temp VALUE='T'>" if request.perm_or_temp == 'T': print "CHECKED" print "> Temporary" return printed ---- Thanks. --john
John Adams wrote:
If I was doing this in perl it'd be quite easy; I could check the query variable, and act on it -- but now I have to deal with things like not being able to even examine the variable without throwing an exception, and the fact that Python doesn't short-circuit blows.
i.e. You can't examine something like:
if (hasattr(request,'perm_or_temp') and request.perm_or_temp == 'P')
Python does indeed short-circuit this statement and it will work as you seem to expect. Have you tried it? In DTML, here is something that will do what I think you want. <dtml-let button_value="REQUEST.get('perm_or_temp')"> <INPUT TYPE='RADIO' NAME=perm_or_temp VALUE="P" <dtml-if "button-value=='P' or button_value==None"> CHECKED</dtml-if> > Permanent <INPUT TYPE='RADIO' NAME=perm_or_temp VALUE="T" <dtml-if "button-value=='T'> CHECKED</dtml-if> > Temporary </dtml-let> There's a million variations on this, some better than others. In Python one way to do this would be as: button_value = context.REQUEST.get('perm_or_temp') checked_button = {None: 'P', 'P':'P', 'T':'T'}.get(button_value) for value in ['P', 'T']: checked = (value == checked_button) and ' CHECKED' or '' print ("<input type=radio name=perm_or_temp VALUE='%s'%s>" % (value, checked)) return printed You could do something similar to this in DTML if you wanted as well. Since there are only a couple of possible values, it's probably easier just to spell it all out in this case. I'd suggest picking up the "Learning Python" O'Reilly book to learn basic idioms like this. The thing you were missing was that dictionary-like objects (of which REQUEST is one) have a get method that returns None if nothing of that key exists in the dictionary on a lookup. -- Chris McDonough Zope Corporation http://www.zope.org http://www.zope.com "Killing hundreds of birds with thousands of stones"
Whoops. I had it wrong last time around. One more time, with feeling: <dtml-let button_value="REQUEST.get('perm_or_temp', 'P')"> <INPUT TYPE='RADIO' NAME=perm_or_temp VALUE="P" <dtml-if "button-value=='P'"> CHECKED</dtml-if> > Permanent <INPUT TYPE='RADIO' NAME=perm_or_temp VALUE="T" <dtml-if "button-value=='T'> CHECKED</dtml-if> > Temporary </dtml-let> .. or .. button_value = context.REQUEST.get('perm_or_temp', 'P') possible = {'P':'Persistent','T':'Temporary'} items = possible.items() items.sort() for key, value in items: checked = (key == button_value) and ' CHECKED' or '' print ("<input type=radio name=perm_or_temp VALUE='%s'%s> %s" % (key, checked, value)) return printed .. and a bonus alternate DTML implementation mirroring the Python one .. <dtml-let button_value="REQUEST.get('perm_or_temp', 'P')" possible="{'P':'Persistent','T':'Temporary'}" items="possible.items()"> <dtml-call "items.sort()"> <dtml-in items> <dtml-let "checked = (_[sequence-key] == button_value) and ' CHECKED' or ''> <INPUT TYPE='RADIO' NAME=perm_or_temp VALUE="<dtml-var sequence-key>" <dtml-var checked>> <dtml-var sequence-item> </dtml-let> </dtml-in> </dtml-let> Chris McDonough wrote:
John Adams wrote:
If I was doing this in perl it'd be quite easy; I could check the query variable, and act on it -- but now I have to deal with things like not being able to even examine the variable without throwing an exception, and the fact that Python doesn't short-circuit blows.
i.e. You can't examine something like:
if (hasattr(request,'perm_or_temp') and request.perm_or_temp == 'P')
Python does indeed short-circuit this statement and it will work as you seem to expect. Have you tried it?
In DTML, here is something that will do what I think you want.
<dtml-let button_value="REQUEST.get('perm_or_temp')"> <INPUT TYPE='RADIO' NAME=perm_or_temp VALUE="P" <dtml-if "button-value=='P' or button_value==None"> CHECKED</dtml-if> > Permanent <INPUT TYPE='RADIO' NAME=perm_or_temp VALUE="T" <dtml-if "button-value=='T'> CHECKED</dtml-if> > Temporary </dtml-let>
There's a million variations on this, some better than others. In Python one way to do this would be as:
button_value = context.REQUEST.get('perm_or_temp') checked_button = {None: 'P', 'P':'P', 'T':'T'}.get(button_value) for value in ['P', 'T']: checked = (value == checked_button) and ' CHECKED' or '' print ("<input type=radio name=perm_or_temp VALUE='%s'%s>" % (value, checked)) return printed
You could do something similar to this in DTML if you wanted as well. Since there are only a couple of possible values, it's probably easier just to spell it all out in this case.
I'd suggest picking up the "Learning Python" O'Reilly book to learn basic idioms like this. The thing you were missing was that dictionary-like objects (of which REQUEST is one) have a get method that returns None if nothing of that key exists in the dictionary on a lookup.
-- Chris McDonough Zope Corporation http://www.zope.org http://www.zope.com "Killing hundreds of birds with thousands of stones"
On Wed, 8 May 2002 13:44, Chris McDonough wrote:
.. and a bonus alternate DTML implementation mirroring the Python one ..
<dtml-let button_value="REQUEST.get('perm_or_temp', 'P')" possible="{'P':'Persistent','T':'Temporary'}" items="possible.items()"> <dtml-call "items.sort()"> <dtml-in items> <dtml-let "checked = (_[sequence-key] == button_value) and ' CHECKED' or ''> <INPUT TYPE='RADIO' NAME=perm_or_temp VALUE="<dtml-var sequence-key>" <dtml-var checked>> <dtml-var sequence-item> </dtml-let> </dtml-in> </dtml-let>
How about a ZPT example to finish the set? I'd do it, but it's been too long since I was allowed to do ZPT :) Richard
Richard Jones wrote:
How about a ZPT example to finish the set? I'd do it, but it's been too long since I was allowed to do ZPT :)
I thought about it but then started it and lost interest when I realized I really don't know how to use the repeat attribute. ;-)
On Wednesday 08 May 2002 5:41 am, Richard Jones wrote:
On Wed, 8 May 2002 13:44, Chris McDonough wrote:
.. and a bonus alternate DTML implementation mirroring the Python one ..
<dtml-let button_value="REQUEST.get('perm_or_temp', 'P')" possible="{'P':'Persistent','T':'Temporary'}" items="possible.items()"> <dtml-call "items.sort()"> <dtml-in items> <dtml-let "checked = (_[sequence-key] == button_value) and ' CHECKED' or ''> <INPUT TYPE='RADIO' NAME=perm_or_temp VALUE="<dtml-var sequence-key>" <dtml-var checked>> <dtml-var sequence-item> </dtml-let> </dtml-in> </dtml-let>
How about a ZPT example to finish the set? I'd do it, but it's been too long since I was allowed to do ZPT :)
Lots of ways to do it in ZPT, again, but this is roughly how I'd do it off the top of my head. <tal:loop define="perm_or_temp python:request.get('perm_or_temp', 'P')" repeat="radiodata python: ({'value':'P','label':'Permanent'}, {'value':'T','label':'Temporary'})"> <input type="radio" name="perm_or_temp" tal:attributes="value radiodata/value; checked python: perm_or_temp == radiodata/value"> <div tal:replace="radiodata/label">Permanent/Temporary</div> </tal:loop> Harry
Who said Zope was getting more like Perl than Python: There's more than one way to do it ;) ----- Original Message ----- From: "Chris McDonough" <chrism@zope.com> To: "Chris McDonough" <chrism@zope.com> Cc: "John Adams" <jadams@inktomi.com>; <zope@zope.org> Sent: Wednesday, May 08, 2002 4:44 AM Subject: Re: [Zope] Why is it so hard to do simple things?
Whoops. I had it wrong last time around. One more time, with feeling:
<dtml-let button_value="REQUEST.get('perm_or_temp', 'P')"> <INPUT TYPE='RADIO' NAME=perm_or_temp VALUE="P" <dtml-if "button-value=='P'"> CHECKED</dtml-if> > Permanent <INPUT TYPE='RADIO' NAME=perm_or_temp VALUE="T" <dtml-if "button-value=='T'> CHECKED</dtml-if> > Temporary </dtml-let>
.. or ..
button_value = context.REQUEST.get('perm_or_temp', 'P') possible = {'P':'Persistent','T':'Temporary'} items = possible.items() items.sort() for key, value in items: checked = (key == button_value) and ' CHECKED' or '' print ("<input type=radio name=perm_or_temp VALUE='%s'%s> %s" % (key, checked, value)) return printed
.. and a bonus alternate DTML implementation mirroring the Python one ..
<dtml-let button_value="REQUEST.get('perm_or_temp', 'P')" possible="{'P':'Persistent','T':'Temporary'}" items="possible.items()"> <dtml-call "items.sort()"> <dtml-in items> <dtml-let "checked = (_[sequence-key] == button_value) and ' CHECKED' or ''> <INPUT TYPE='RADIO' NAME=perm_or_temp VALUE="<dtml-var sequence-key>" <dtml-var checked>> <dtml-var sequence-item> </dtml-let> </dtml-in> </dtml-let>
Chris McDonough wrote:
John Adams wrote:
If I was doing this in perl it'd be quite easy; I could check the query variable, and act on it -- but now I have to deal with things like not being able to even examine the variable without throwing an exception, and the fact that Python doesn't short-circuit blows.
i.e. You can't examine something like:
if (hasattr(request,'perm_or_temp') and request.perm_or_temp == 'P')
Python does indeed short-circuit this statement and it will work as you seem to expect. Have you tried it?
In DTML, here is something that will do what I think you want.
<dtml-let button_value="REQUEST.get('perm_or_temp')"> <INPUT TYPE='RADIO' NAME=perm_or_temp VALUE="P" <dtml-if "button-value=='P' or button_value==None"> CHECKED</dtml-if> > Permanent <INPUT TYPE='RADIO' NAME=perm_or_temp VALUE="T" <dtml-if "button-value=='T'> CHECKED</dtml-if> > Temporary </dtml-let>
There's a million variations on this, some better than others. In Python one way to do this would be as:
button_value = context.REQUEST.get('perm_or_temp') checked_button = {None: 'P', 'P':'P', 'T':'T'}.get(button_value) for value in ['P', 'T']: checked = (value == checked_button) and ' CHECKED' or '' print ("<input type=radio name=perm_or_temp VALUE='%s'%s>" % (value, checked)) return printed
You could do something similar to this in DTML if you wanted as well. Since there are only a couple of possible values, it's probably easier just to spell it all out in this case.
I'd suggest picking up the "Learning Python" O'Reilly book to learn basic idioms like this. The thing you were missing was that dictionary-like objects (of which REQUEST is one) have a get method that returns None if nothing of that key exists in the dictionary on a lookup.
-- Chris McDonough Zope Corporation http://www.zope.org http://www.zope.com "Killing hundreds of birds with thousands of stones"
_______________________________________________ Zope maillist - Zope@zope.org http://lists.zope.org/mailman/listinfo/zope ** No cross posts or HTML encoding! ** (Related lists - http://lists.zope.org/mailman/listinfo/zope-announce http://lists.zope.org/mailman/listinfo/zope-dev )
On Tue, May 07, 2002 at 11:44:52PM -0400, Chris McDonough wrote:
button_value = context.REQUEST.get('perm_or_temp', 'P')
quick note to John A: the above works with dictionaries and dictionary-like objects. somedict.get('keyname', 'default result') ... will return the value indexed by 'keyname', or the default result you provide. The default is None by default. With other kinds of objects you can use the built-in getattr function in the same way: getattr(myobject, 'attrname', 'default result') When learning python, I *highly* recommend having a window open with an interactive python session going. If you're not sure what something does, try it out - "ask the interpreter", as we say. It's often faster than looking something up in a book or manual. And you can find out all sorts of things using the dir() builtin function, and looking at the __doc__ attributes of classes, functions, and methods. Example: $ python Python 1.5.2 (#1, Aug 25 2000, 09:33:37) [GCC 2.96 20000731 (experimental)] on linux-i386 Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
dir() ['__builtins__', '__doc__', '__name__'] dir(__builtins__) ['ArithmeticError', 'AssertionError', 'AttributeError', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'FloatingPointError', 'IOError', 'ImportError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplementedError', 'OSError', 'OverflowError', 'RuntimeError', 'StandardError', 'SyntaxError', 'SystemError', 'SystemExit', 'TypeError', 'ValueError', 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', '__name__', 'abs', 'apply', 'buffer', 'callable', 'chr', 'cmp', 'coerce', 'compile', 'complex', 'delattr', 'dir', 'divmod', 'eval', 'execfile', 'exit', 'filter', 'float', 'getattr', 'globals', 'hasattr', 'hash', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'len', 'list', 'locals', 'long', 'map', 'max', 'min', 'oct', 'open', 'ord', 'pow', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'round', 'setattr', 'slice', 'str', 'tuple', 'type', 'vars', 'xrange'] print getattr.__doc__ getattr(object, name[, default]) -> value
Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y. When a default argument is given, it is returned when the attribute doesn't exist; without it, an exception is raised in that case.
print round.__doc__ round(number[, ndigits]) -> floating point number
Round a number to a given precision in decimal digits (default 0 digits). This always returns a floating point number. Precision may be negative.
-- Paul Winkler "Welcome to Muppet Labs, where the future is made - today!"
On Tue, 7 May 2002, Chris McDonough wrote:
Python does indeed short-circuit this statement and it will work as you seem to expect. Have you tried it?
Yes, it fails with attribute errors, but that was as part of a dtml-if.
In DTML, here is something that will do what I think you want.
Thanks, I'll try this.
Since there are only a couple of possible values, it's probably easier just to spell it all out in this case.
I'd suggest picking up the "Learning Python" O'Reilly book to learn basic idioms like this. The thing you were missing was that dictionary-like objects (of which REQUEST is one) have a get method that returns None if nothing of that key exists in the dictionary on a lookup.
Oh! Cool, so if it's accessed with the REQUEST method, i'll get none, and directly (as an attribute) it'll fail then? -john -- John Adams . Sr. Security Engineer . Inktomi Corporation
On Wed, 8 May 2002 10:12, John Adams wrote:
I'm new to Zope, and new to Python, but certainly not new to software engineering, with most of my background in C, Perl and Java.
That's nice.
If I was doing this in perl it'd be quite easy; I could check the query variable, and act on it -- but now I have to deal with things like not being able to even examine the variable without throwing an exception, and the fact that Python doesn't short-circuit blows.
i.e. You can't examine something like:
if (hasattr(request,'perm_or_temp') and request.perm_or_temp == 'P') ...
In Zope, I assume that I should be using DTML here, and perhaps dtml-in over the items in the radio list.
Number one suggestion: have the page call a python script method that checks the environment for a form submission as the first thing it does. If it doesn't find a form submission, have it set the environment to your sensible form defaults. Your presentation/view code (DTML or ZPT) now doesn't have to have lots of yucky code in it to handle the various with/without cases.
# Default block -- this is a really shitty way to do this sort # of thing. I hate python.
Number two suggestion: take your attitude and shove it up your arse.
if not hasattr(request,'perm_or_temp'): print "<INPUT TYPE='RADIO' NAME=perm_or_temp VALUE='P' CHECKED> Permanent" print "<INPUT TYPE='RADIO' NAME=perm_or_temp VALUE='T'> Temporary" return printed
# else we have it, process the request print "<INPUT TYPE='RADIO' NAME=perm_or_temp VALUE='P'>"
if request.perm_or_temp == 'P'): print "CHECKED"
print "> Permanent"
print "<INPUT TYPE='RADIO' NAME=perm_or_temp VALUE='T'>"
if request.perm_or_temp == 'T': print "CHECKED"
print "> Temporary"
return printed
If you don't want to split the model and view code as I suggested above, try this python script code for generating radio boxes. perm_or_temp = getattr(request, 'perm_or_temp', 'P') for value, label in (('P', 'Permanent'), ('T', 'Temporary')): if perm_or_temp == value: checked = 'CHECKED' else: checked = '' print '<INPUT TYPE="RADIO" NAME="perm_or_temp" VALUE="%s" %s>%s'%(value, checked, label) Richard
On Wed, 8 May 2002, Richard Jones wrote:
On Wed, 8 May 2002 10:12, John Adams wrote: Number one suggestion: have the page call a python script method that checks the environment for a form submission as the first thing it does. If it doesn't find a form submission, have it set the environment to your sensible form defaults. Your presentation/view code (DTML or ZPT) now doesn't have to have lots of yucky code in it to handle the various with/without cases.
This is the general consensus, so far.
# Default block -- this is a really shitty way to do this sort # of thing. I hate python. Number two suggestion: take your attitude and shove it up your arse.
Geesh, I've already apologized to three people for one frustrated comment. I didn't say you and everyone else who uses Python sucks, because -I- hate python (because I can't figure out how to do what I want to do). I said that at that moment, *I* hated python. Relax.
If you don't want to split the model and view code as I suggested above, try this python script code for generating radio boxes.
Thanks for all the help. I'll give it a try. -john
From: "John Adams" <jadams@inktomi.com>
If I was doing this in perl it'd be quite easy; I could check the query variable, and act on it -- but now I have to deal with things like not being able to even examine the variable without throwing an exception
This has nothing to do with either Zope or Python. It has to do with how HTML forms are done, and yes, I agree that the people designing that standard are a bunch of morons. Checkboxes in form submissions has only one state. Yes, thats stupid, but thats how it is. Either they are checked or they don't exist at all. It is therefore impossible to know from the form submission whether a certain variable is unchecked or simply does not exist. There are basically two solutions to this. 1. Define all the variables in the python method called, and let have defaults on the optional variables. LIke so: def form_submission_method( self, variable1, variable2, thidisacheckbox=None, REQUEST): #Now you can be sure there is a "thisisacheckbox" variable, and it will either be set, or it will be None if thisisacheckbox: return "It's checked!" 2. Do a check like this: if hasattr(request,'perm_or_temp') and request.perm_or_temp == 'P': Btw, when doing forms, I suggest you look at formulator. It helps you validate forms. Best Regards Lennart Regebro Torped Strategi och Kommunikation AB http://easypublisher.torped.se/english/
On Wed, 8 May 2002, Lennart Regebro wrote:
From: "John Adams" <jadams@inktomi.com>
If I was doing this in perl it'd be quite easy; I could check the query variable, and act on it -- but now I have to deal with things like not being able to even examine the variable without throwing an exception
This has nothing to do with either Zope or Python. It has to do with how HTML forms are done, and yes, I agree that the people designing that standard are a bunch of morons.
Ahh, we're confused here, what I meant by my question was: The first time it's sent, if noone's checked anything yet, then check the first radio button by default. Now, granted, I do agree with you that CGI processing is just stupid -- if noone checks the checkbox, you never get the form variable back AT ALL, and that's bad design on the part of the protocol.
There are basically two solutions to this.
1. Define all the variables in the python method called, and let have defaults on the optional variables. LIke so:
def form_submission_method( self, variable1, variable2, thidisacheckbox=None, REQUEST): #Now you can be sure there is a "thisisacheckbox" variable, and it will either be set, or it will be None if thisisacheckbox: return "It's checked!"
2. Do a check like this: if hasattr(request,'perm_or_temp') and request.perm_or_temp == 'P':
Btw, when doing forms, I suggest you look at formulator. It helps you validate forms.
Okay, I'll beat on this at work tomorrow. I've got a few good solutions to try now. Thanks for your help. --john -- John Adams . Sr. Security Engineer . Inktomi Corporation
[Lennart Regebro]
From: "John Adams" <jadams@inktomi.com>
If I was doing this in perl it'd be quite easy; I could check the query variable, and act on it -- but now I have to deal with things like not being able to even examine the variable without throwing an exception
This has nothing to do with either Zope or Python. It has to do with how HTML forms are done, and yes, I agree that the people designing that standard are a bunch of morons.
Checkboxes in form submissions has only one state. Yes, thats stupid, but thats how it is. Either they are checked or they don't exist at all. It is therefore impossible to know from the form submission whether a certain variable is unchecked or simply does not exist.
There are basically two solutions to this.
There's another solution, one that I used on a job awhile ago. Use the :list syntax for the checkbox name, then test the return value (an empty list, [], returns 0 while a non-empty list returns 1): <input type="checkbox" name="live:list" value='checked'> Simple and reasonable clean. Also it's a very nice Zope feature that you wouldn't get with ordinary CGI. Cheers, Tom P
On Wed, 8 May 2002, Chris Withers wrote:
John Adams wrote:
# Default block -- this is a really shitty way to do this sort # of thing. I hate python.
If you hate Python, why are you using Zope?
Oh, I'm evaluating it to see if it's going to work for what we want to do here. There's lots of good features of both Python and Zope, but I guess I'm currently uncomfortable with the way you're forced to access objects around and it's column-specific scoping. Ignore my frustrated comment -- I had been working on the problem for a few hours at that point anyway. :) -john
participants (9)
-
Chris McDonough -
Chris Withers -
Harry Wilkinson -
John Adams -
Lennart Regebro -
Paul Winkler -
Phil Harris -
Richard Jones -
Thomas B. Passin