Multiple buttons on a single form

5 views
Skip to first unread message

Suren

unread,
Dec 7, 2006, 10:21:59 AM12/7/06
to TurboGears
If I have multiple buttons on a form, how does the controller which
button has been clicked? I can send all submits to one controller
method but how to determine which of those four buttons were clicked?

Christopher Arndt

unread,
Dec 7, 2006, 11:20:43 AM12/7/06
to turbo...@googlegroups.com
Suren schrieb:

> If I have multiple buttons on a form, how does the controller which
> button has been clicked? I can send all submits to one controller
> method but how to determine which of those four buttons were clicked?

Just test for the existence of the form variable with the name given to the
'name' attribute of the button:

class Controller:
@expose(self, **kw):
if kw.get('action_kung'):
# do action kung
elif kw.get('action_foo'):
# do action foo

where your form would look like:

<form>
....
<input type="submit" name="action_foo" value="Do Kung!">
<input type="submit" name="action_foo" value="Do Foo!">
</form>

The test for the existence of the submit button should really be

kw.get('action_foo') is not None

but since form variables will be always strings, unless you mangle them with
validators, there is no danger that a 0 (zero) integer value will be
interpreted as False.


Explanation:

You test for the name rather for the value of the submit button, because submit
buttons display their value as the button label, which will probably change in
your application according to i18n, so you can't test for it in your controller.

Normally, one should be able to use <button type="submit"> elements instead,
since these can contain any element, but stupid IE will always submit values
for *all* <button> elements in a form, regardless of which one you click!

You can test this with this simple form:

<form>
<input type="text" name="text" size="50">

<button name="nosubmit">
This does nothing
</button>

<button type="submit" name="kung">
Do Kung!
</button>
<button type="submit" name="foo">
Do Foo!
</button>

</form>

HTH, Chris

Christopher Arndt

unread,
Dec 7, 2006, 11:25:09 AM12/7/06
to turbo...@googlegroups.com
Christopher Arndt schrieb:

> where your form would look like:
>
> <form>
> ....
> <input type="submit" name="action_foo" value="Do Kung!">
> <input type="submit" name="action_foo" value="Do Foo!">
> </form>

Sorry, that should be:

<form>
....
<input type="submit" name="action_kung" value="Do Kung!">


<input type="submit" name="action_foo" value="Do Foo!">
</form>

Chris

Suren

unread,
Dec 7, 2006, 11:31:38 AM12/7/06
to TurboGears
Thanks much for your explanation.

Christopher Arndt

unread,
Dec 7, 2006, 11:28:12 AM12/7/06
to turbo...@googlegroups.com
Christopher Arndt schrieb:

> class Controller:
> @expose(self, **kw):
> if kw.get('action_kung'):
> # do action kung
> elif kw.get('action_foo'):
> # do action foo

Argh, where's my head:

class Controller:
@expose()
def index(self, **kw):


if kw.get('action_kung'):
# do action kung
elif kw.get('action_foo'):
# do action foo

*now goes to make dinner so that brain gets some blood sugar again*

Chris

Christopher Arndt

unread,
Dec 7, 2006, 1:34:18 PM12/7/06
to turbo...@googlegroups.com
Suren schrieb:

> If I have multiple buttons on a form, how does the controller which
> button has been clicked? I can send all submits to one controller
> method but how to determine which of those four buttons were clicked?

BTW, for the most common case, where you want a submit and a cancel button, you
can just put a link instead of the cancel button, that references another url
than the form action, and then make it look like a button with CSS:

<style>
a.button {
color: black;
border: .2em outset #e2e2de;
background-color: #e2e2de;
font-family: sans-serif;
font-size: small;
text-decoration: none;
padding: .15em .5em;
display: inline-block;
cursor: default;
}

a.button:active {
border-style: inset;
}
</style>

<input type="submit" value="A real button">&nbsp;
<a class="button" href="#">A fake button</a>

Of course, you should then style normal buttons accordingly too.

Chris

Suren

unread,
Dec 7, 2006, 2:39:21 PM12/7/06
to TurboGears
I figured you had missed the def part. Thanks for clarification.

Marco Mariani

unread,
Dec 7, 2006, 2:53:34 PM12/7/06
to turbo...@googlegroups.com
Christopher Arndt wrote:
> Suren schrieb:
>
>> If I have multiple buttons on a form, how does the controller which
>> button has been clicked? I can send all submits to one controller
>> method but how to determine which of those four buttons were clicked?
>>
>
> BTW, for the most common case, where you want a submit and a cancel button, you
> can just put a link instead of the cancel button, that references another url
> than the form action, and then make it look like a button with CSS:
>
If you do that, be sure your "fake" button does not cause writes to a DB
or something like that... we're not supposed to change the state of a
server application upon using a GET method on an URL. There are several
caching-plugins, extensions and spiders that happily open every link
they see on a loaded page without user action, and in that case they
would change or delete data on the server.


Adam Jones

unread,
Dec 7, 2006, 3:25:05 PM12/7/06
to TurboGears

IIRC basecamp (made by the Ruby on Rails team) had this problem when
google's web accelerator went click-happy and deleted a bunch of stuff.
Moral: Even on a restricted site you should beware of this kind of
thing. You can restyle links and buttons so one looks like the other,
that is pretty much always a better solution than using one instead of
the other.

-Adam

chiangf

unread,
Dec 8, 2006, 7:46:06 PM12/8/06
to TurboGears
A slight spinoff of this question... suppose that I wanted to call
another function within the index def? If I just borrow the code from
above... is it possible to call a function kung (or foo, doesn't
matter), from the index function (and pass **kw along with it)?

class Controller:
@expose()
def index(self, **kw):
if kw.get('action_kung'):

# do action kung, but kung is another function
elif kw.get('action_foo'):
# do action foo, but foo is another function

@expose()
def kung(self, **kw):
...

@expose()
def foo(self, **kw):
...

Alberto Valverde

unread,
Dec 9, 2006, 6:05:01 AM12/9/06
to turbo...@googlegroups.com

On Dec 9, 2006, at 1:46 AM, chiangf wrote:

>
> A slight spinoff of this question... suppose that I wanted to call
> another function within the index def? If I just borrow the code from
> above... is it possible to call a function kung (or foo, doesn't
> matter), from the index function (and pass **kw along with it)?

Sure, why not? :) In fact, this is precisely what errorhandling does
when calling the error handler after validation fails or an exception
occurs.

Alberto

chiangf

unread,
Dec 9, 2006, 10:49:27 AM12/9/06
to TurboGears
I tried doing something like (not sure if this is what you meant?):

class Controller:
@expose()
def index(self, **kw):
if kw.get('action_kung'):
self.kung(**kw)
elif kw.get('action_foo'):
self.foo(**kw)

@expose(sometemplate1)
def kung(self, **kw):
...
return dict(kw=kw)

@expose(sometemplate2)
def foo(self, **kw):
...
return dict(kw=kw)


Where I want kung to display sometemplate1 and foo to display
sometemplate2. But if I un the above code, then it calls kung (or foo)
and then returns back to index() and continues... is there any way to
redirect to kung so that sometemplate1 is displayed?

Did that make sense? I was actually getting lost writing my own
question, :)


Frank

Alberto Valverde

unread,
Dec 9, 2006, 11:23:44 AM12/9/06
to turbo...@googlegroups.com

You need to return the output of the method you're calling:

@expose()
def index(self, **kw):
if kw.get('action_kung'):

return self.kung(**kw)
----------^^^^^
elif kw.get('action_foo'):
return self.foo(**kw)

Alberto

Adam Jones

unread,
Dec 9, 2006, 12:20:05 PM12/9/06
to TurboGears

Keep in mind that you also need to set a tg_template key in your return
dictionary if you want to override the template that is used. So to
have index use kung's template you would use code like:

@expose()
def index(self, **kw):
if kw.get('action_kung'):
return

self.kung(**kw).update(dict(tg_template="sometemplate1"))

-Adam

>
> Alberto

chiangf

unread,
Dec 9, 2006, 5:41:40 PM12/9/06
to TurboGears
Thanks guys! I'm not sure why though but neither way worked... In the
first, kw didn't get passed for some reason and in the second, it
complained that 'str' does not have attribute 'update'

But I found one of Adam's other threads: http://tinyurl.com/t9osu,
which was exactly what I wanted.

Thanks again


Frank

chiangf

unread,
Dec 9, 2006, 6:26:14 PM12/9/06
to TurboGears
Sorry for posting twice, but suddenly both methods work... not sure
what I did differently

Christopher Arndt

unread,
Dec 10, 2006, 4:36:05 AM12/10/06
to turbo...@googlegroups.com
Adam Jones wrote:
> Keep in mind that you also need to set a tg_template key in your return
> dictionary if you want to override the template that is used. So to
> have index use kung's template you would use code like:
>
> @expose()
> def index(self, **kw):
> if kw.get('action_kung'):
> return
> self.kung(**kw).update(dict(tg_template="sometemplate1"))

This will not work as expected. The update method of dict does not
return the updated dict. Correct:

@expose()
def index(self, **kw):
if kw.get('action_kung'):

kw.update(dict(tg_template="sometemplate1"))
return self.kung(**kw)

Chris

Alberto Valverde

unread,
Dec 10, 2006, 7:34:11 AM12/10/06
to turbo...@googlegroups.com

Hmmm, not really. self.kung will return a string because it's already
decorated by "expose" so you can't really do that. To get the dict
self.kung would have returned un-decorated you have to undecorate it
yourself before calling it (take a look at turbogears.decorator for a
function called func_orig (or something like that, can't check that out
ATM cause I'm in public access point (with IE6 and a 15" CRT... argggg))
which does that. That way you will return a dict and index's "expose" will
process that dict (instead of "kung"'s)

Alberto

Lee McFadden

unread,
Dec 10, 2006, 8:51:47 AM12/10/06
to turbo...@googlegroups.com
On 12/9/06, chiangf <chi...@gmail.com> wrote:
>
> I tried doing something like (not sure if this is what you meant?):
> class Controller:
---8<--- snip ---8<---

>
> Where I want kung to display sometemplate1 and foo to display
> sometemplate2. But if I un the above code, then it calls kung (or foo)
> and then returns back to index() and continues... is there any way to
> redirect to kung so that sometemplate1 is displayed?
>
> Did that make sense? I was actually getting lost writing my own
> question, :)
>
>

I think the best way (imho) is to make sure each method returns its
own tg_template value. Also, unless you're going to be using them via
their own URL's as well, I wouldn't use @expose() on the kung and foo
methods:

class Controller(controllers.Controller):


@expose()
def index(self, **kw):
if kw.get('action_kung'):

return self.kung(**kw)
elif kw.get('action_foo'):
return self.foo(**kw)

def kung(self, **kw):
# Note the complete lack of @expose()
# Do stuff here
return dict(tg_template=".templates.kung", kw=kw)

def foo(self, **kw):
# No @expose() again
return dict(tg_template=".templates.foo", kw=kw)

Lee

--
Lee McFadden

blog: http://www.splee.co.uk
work: http://fireflisystems.com
skype: fireflisystems

Reply all
Reply to author
Forward
0 new messages