[Twisted-web] Cascading forms in formal (example code included)

Matt Goodall matt at pollenation.net
Wed Jun 14 16:07:48 CDT 2006


Ricky Iacovou wrote:
> Hi there.
> 
> I've been wrestling with a Nevow/formal problem for a day now (though it might 
> not be formal-specific); I'm hoping someone with more Nevow experience might 
> be able to shed some light on the problem.
> 
> I've reduced the problem to the Python code below; run it with 'twistd -noy 
> file.py' and point your browser to localhost:8080.
> 
> The first page is a form; anything you enter gets handled by 
> FirstPage.handler(). I want to have this form lead to another form which asks 
> for further information based on the input to the first form.
> 
> If you enter the string 'test' in the first page, you will go through an 
> intermediate static page. Anything else will take you directly to another 
> form.
> 
> Tv on #twisted.web pointed out that I can return an IResource from a form 
> handler. I'm trying to do this here: I create a SecondPage() object and 
> return that, but the server goes into an infinite loop, constantly handling 
> the first page results (printing, "User string was: " ...).
> 
> [In my actual code, I can Ctrl-C the server at this point; in this example 
> code, I actually have to Ctrl-Z and kill -9 the server. I don't know why.]
> 
> Note that using the static intermediate page (i.e. simply return a TestPage() 
> object from FirstPage.handler()) works peachily. In fact, when you access a 
> SecondPage instance as a child of the FirstPage (there's a link on the static 
> page), it also works.
> 
> Does anyone have any idea why returning the second form page directly creates 
> an infinite loop?


I suspect it's because when SecondPage sees the request was POST'ed and 
assumes it's a form POST. Processing the POST will re-find the 
FirstPage's form and processes it again which returns a SecondPage, etc.

I'm not sure if that's a bug or a feature/limitation ;-).

Even if SecondPage was rendered correctly after submitting FirstPage, I 
think Formal would have a hard time finding SecondPage's form because 
the URL would resolve to a FirstPage resource.

Interesting.

I'll add a ticket and have a think about it.

The way I've done this sort of thing is to make FirstPage HTTP redirect 
to another URL to display SecondPage, passing data from FirstPage to 
SecondPage using request args. (Hint: return a nevow.url.URL instance 
from FirstPage's form's submit method.)

If there's a lot of data to pass around you might need to resort to 
sessions. Yucky but, hey, it's the web.

It would certainly be nicer to chain form pages together.

Hope this helps, even if it doesn't solve your problem.

- Matt


> 
> Thanks,
> 
> Ricky
> 
> --
> Code starts here
> --
> 
> from twisted.python import util
> from twisted.internet import defer
> from twisted.application import strports
> from twisted.application import service
> 
> from nevow import appserver
> from nevow import rend
> from nevow import loaders
> from nevow import tags as T
> from nevow import url
> 
> import formal
> 
> 
> class SecondPage ( formal.ResourceMixin, rend.Page ):
> 
>     def form_example ( self, ctx ):
>         form = formal.Form()
>         form.addField ( 'description',
>                         formal.String(),
>                         label = "Type Stuff",
>                         description = "For example, 'marmalade'"
>                         )
> 
>         form.addAction ( self.handler, label = "Go" )
>         return form
> 
> 
>     def handler ( self, ctx, form, data ):
>         print "Form data was: ", data
>         # End of form chain; just return a static page.
>         return TestPage()
> 
> 
>     docFactory = loaders.stan (
>         T.html [ T.head [ T.title [ "Second Page" ] ],
>                  T.body [ T.directive ( 'form example' ) ] ] )
> 
> 
> 
> 
> class FirstPage ( formal.ResourceMixin, rend.Page ):
> 
>     def form_example ( self, ctx ):
>         form = formal.Form()
>         form.addField ( 'user_string',
>                         formal.String(),
>                         label = "Type Stuff",
>                         description = "Type 'test' to go to TestPage."
>                         )
> 
>         form.addAction ( self.handler, label = "Go" )
>         return form
> 
> 
>     def handler ( self, ctx, form, data ):
>         user_string = data [ 'user_string' ]
>         print "User string was: ", user_string
> 
>         if str ( user_string ) == 'test':
>             # This works:
>             page = TestPage()
>         else:
>             # ... but this doesn't:
>             page = SecondPage()
> 
>         # Also, how to pass in the 'state' (i.e. user_string)? Should we pass
>         # it as a construction argument of the Page?
> 
>         # In the Real Code we make a call to an XML-RPC server in the handler,
>         # which is why we return a deferred here rather than the page itself.
>         return defer.succeed ( page )
> 
> 
>     # Instantiate a SecondPage so it can be accessed indirectly.
>     child_secondPage = SecondPage()
> 
> 
>     docFactory = loaders.stan (
>         T.html [ T.head [ T.title [ "First Page" ] ],
>                  T.body [ T.directive ( 'form example' ) ] ] )
> 
> 
> class TestPage ( rend.Page ):
>     docFactory = loaders.stan (
>         T.html [ T.head [ T.title [ "Test Page" ] ],
>                  T.body [ "Visit the ",
>                           T.a ( href = '/secondPage' ) [ "Second Page" ] ] ] )
> 
> 
> application = service.Application ( 'Broken Forms' )
> site        = appserver.NevowSite ( resource = FirstPage() )
> server      = strports.service ( "8080", site )
> server.setServiceParent ( application )
> 
> 
> --
> Code ends here
> --
> 
> _______________________________________________
> Twisted-web mailing list
> Twisted-web at twistedmatrix.com
> http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web


-- 
      __
     /  \__     Matt Goodall, Pollenation Internet Ltd
     \__/  \    w: http://www.pollenation.net
   __/  \__/    e: matt at pollenation.net
  /  \__/  \    t: +44 (0)113 2252500
  \__/  \__/
  /  \	       Any views expressed are my own and do not necessarily
  \__/          reflect the views of my employer.



More information about the Twisted-web mailing list