[Product-Developers] Re: Traversing from a z3c.form action handler
senger at rehfisch.de
Sun Feb 14 20:35:54 UTC 2010
--On Sonntag, Februar 14, 2010 10:24:21 -0800 cewing
<cewing at u.washington.edu> wrote:
> Martin Aspeli wrote:
>> Cristopher Ewing wrote:
>>> I'm trying to work out a way to have the action in a z3c.form based form
>>> send the user to the built-in plone content_status_history form, similar
>>> to how the 'Change State' action on folder_contents works.
>>> For some reason I can't seem to find any examples of making this work.
>>> Can someone help me figure out what the right way is to accomplish this
>>> task? I need to use the form action handler to set the 'paths' parameter
>>> on the request, so that request.get('paths') will return the paths of
>>> the objects I've selected in my form. I can do that, but once it's done,
>>> how to I traverse on to the content_status_history form? That part has
>>> me stumped.
>> Did you try self.request.response.redirect(self.context.absolute_url() +
>> '/content_status_history') ?
> It was 3 am when I got to this point, but yes, that was the first thing I
> tried. Response.redirect appears to lose the data I've set on the request
> in my form action. I remember reading a blog post at one point about
> using a traversal technique instead of response.redirect to keep the
> original request and all its data intact as you move from one page to
> another. It seems that ControllerPageTemplates must use something like
> this, since all of their metadata contain references to 'TraverseTo'.
As you found out it's a special implementation in CMFFormController.
> I'm not terrifically concerned about the URL being wrong, but I've not
> managed to have returning the URL do anything.
> Here's what I have in the action so far:
> class AnimalTable(HydraCrudEditForm):
> label = _(u'My Animals')
> @button.buttonAndHandler(u'Change State', name='state_change')
> def handle_state_change(self, action):
> """ traverse to the content_status_share form for processing
> true_context = self.context.context
> selected = self.selected_items()
> paths = 
> if selected:
> paths = [item.absolute_url_path() for id, item in selected]
> self.request.form['paths'] = paths
> path = true_context.absolute_url_path() +
> state_view = true_context.restrictedTraverse(path)
> return state_view()
> IStatusMessage(self.request).add("No animals selected",
> Unfortunately, that just ends up by reloading the original form without
> any indication that anything has happened.
z3c.form.Form.__call__() calls .update() which runs .execute() on the
actions. Return values are ignored. You have to take the proper action
In the current implementation, plone.z3cform FormWrapper is called
before the form itself is processed. It will write html into the response
before you have the chance to return html from your form. Without
customizing the form wrapper, you can only use redirect with the
limitations you pointed out.
You can use a custom FormWrapper that updates your form and does not
render the templates in certain cases. You can find an example in
BadgeFormWrapper. It subclasses the original FormWrapper, calls
FormWrapper.contents() which processes the form completely. Then it checks
an attribute of the form instance and can return pdf data. If the form did
not produce a pdf, it uses the original FormWrapper.__call__ to get the
normal html output. It's here:
More information about the Product-Developers