[Twisted-Python] again on nested sequence

Hi. I want to render a list of <select> controls, each with a list of options. I have attached the "simple" solution. Of cource it does (and can't) not work. The problem is that I can't find a better solution. Moreover, with this code I obtain a strange error, log file attached. Thanks Manlio Perillo from twisted.application import service, strports from nevow import appserver, rend, loaders class Main(rend.Page): addSlash = True docFactory = loaders.xmlfile('nestedsequence.xhtml') def __init__(self): # cached self.data_option_list = [('1', 'uno'), ('2', 'due')] def data_control_list(self, ctx, data): return [('a', 'first'), ('b', 'second')] def render_control(self, ctx, data): ctx.fillSlots('ctrl_label', data[1]) ctx.fillSlots('ctrl_name', data[0]) return ctx.tag def render_option(self, ctx, data): ctx.fillSlots('opt_label', data[1]) ctx.fillSlots('opt_value', data[0]) return ctx application = service.Application("nested sequence") site = appserver.NevowSite(Main()) addr = "tcp:8080:interface=127.0.0.1" strports.service(addr, site).setServiceParent(application) 2006/07/16 16:16 GMT [-] Log opened. 2006/07/16 16:16 GMT [-] twistd 2.2.0 (C:\Python2.4\python.exe 2.4.3) starting up 2006/07/16 16:16 GMT [-] reactor class: twisted.internet.selectreactor.SelectReactor 2006/07/16 16:16 GMT [-] nevow.appserver.NevowSite starting on 8080 2006/07/16 16:16 GMT [-] Starting factory <nevow.appserver.NevowSite instance at 0x01158648> 2006/07/16 16:16 GMT [HTTPChannel,0,127.0.0.1] Traceback (most recent call last): File "C:\Python2.4\Lib\site-packages\nevow\rend.py", line 546, in _renderHTTP return self.flattenFactory(doc, ctx, writer, finisher) File "C:\Python2.4\Lib\site-packages\nevow\rend.py", line 506, in <lambda> flattenFactory = lambda self, *args: flat.flattenFactory(*args) File "C:\Python2.4\Lib\site-packages\nevow\flat\__init__.py", line 14, in flattenFactory return deferflatten(stan, ctx, writer).addCallback(finisher) File "C:\Python2.4\Lib\site-packages\nevow\flat\twist.py", line 37, in deferflatten drive() --- <exception caught here> --- File "C:\Python2.4\Lib\site-packages\nevow\flat\twist.py", line 17, in drive next = iterable.next() File "C:\Python2.4\Lib\site-packages\nevow\flat\ten.py", line 84, in iterflatten for item in gen: File "C:\Python2.4\Lib\site-packages\nevow\flat\flatstan.py", line 89, in TagSerializer newdata = convertToData(original.data, context) File "C:\Python2.4\Lib\site-packages\nevow\accessors.py", line 30, in convertToData newdata = olddata.get(context) File "C:\Python2.4\Lib\site-packages\nevow\accessors.py", line 52, in get child = container.child(context, self.original.name) File "C:\Python2.4\Lib\site-packages\nevow\accessors.py", line 128, in child return self.original[int(name)] exceptions.ValueError: invalid literal for int(): option_list 2006/07/16 16:16 GMT [HTTPChannel,0,127.0.0.1] 127.0.0.1 - - [16/Jul/2006:18:16:45 +0000] "GET / HTTP/1.1" 500 12253 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.0.4) Gecko/20060508 Firefox/1.5.0.4" 2006/07/16 16:16 GMT [-] Received SIGINT, shutting down. 2006/07/16 16:16 GMT [-] (Port 8080 Closed) 2006/07/16 16:16 GMT [-] Stopping factory <nevow.appserver.NevowSite instance at 0x01158648> 2006/07/16 16:16 GMT [-] Main loop terminated. 2006/07/16 16:16 GMT [-] Server Shut Down.

On Sun, 16 Jul 2006 16:18:07 -0200, Manlio Perillo <manlio_perillo@libero.it> wrote: This is off topic here... Twisted-web is the list. Anyway: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:n="http://nevow.com/ns/nevow/0.1" lang="it" xml:lang="it"> <head> <title>Nested sequence</title> </head> <body> <ul n:render="sequence" n:data="control_list"> # Here you call data_control_list. # The result of data_control_list becomes the IContainer implementor # from now on inside this <ul> tag. <li n:pattern="item" n:render="control"> # You call the render_control method on the IRenderer implementor which # is the Main instance this method fills 2 slots ctrl_label and ctrl_name. # every slot inside this <li> tag that has one of those 2 names will be # filled unless an inner tag fills one of those slots with a different # value <label><n:slot name="ctrl_label"/> <select n:render="sequence" n:data="option_list"> # Here you call IContainer(ctx).child('option_list') # IContainer(ctx) is what was returned at the beginning with # data_control_list. # That result of that call is a list and the IContainer adapter # for lists does position lookup. # __getitem__ for lists takes integers and since all the values # coming from the template are strings they have to be converted # to integers. The value coming is 'option_list' and this cannot # be converted to an integer. And thus the error. # The solution? Fairly simple, although it would have been better that, # after I explained how part of the rendering works, YOU tried to find # the solution, anyway I'll explain it :). # # There are 2 solutions (actually more but only 2 that keep using templates): # - Return a list of dictionaries from control_list # - Return a list of 3-tuples from control_list # To avoid changing the template too much let's just see how the # previous line should have been for the second option: # <select n:render="sequence" n:data="2"> # That'it. # def data_control_list(self, ctx, data): # return [('a', 'first', ('1', 'uno')), ('b', 'second', ('2', 'due'))] # Of course I hate this solution because it involes too much code... # you already wrote 3 methods where one would have been largerly enough. <n:attr name="name"><n:slot name="ctrl_name" /></n:attr> <option n:pattern="item" n:render="option"> <n:attr name="label"><n:slot name="opt_label" /></n:attr> <n:attr name="value"><n:slot name="opt_value" /></n:attr> </option> </select> </label> </li> </ul> </body> </html> The best solution is the following (NOT TESTED, but should work): class Main(rend.Page): docFactory = loaders.xmlfile('nested.xhtml') def data_control_list(self, ctx, data): return [{'ctrl_name': 'a', 'ctrl_label': 'first', 'option_list': {'opt_value': '1', 'opt_label': 'uno'} }, {'ctrl_name': 'b', 'ctrl_label': 'second', 'option_list': {'opt_value': '2', 'opt_label': 'second'} }] # nested.xhtml <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:n="http://nevow.com/ns/nevow/0.1" lang="it" xml:lang="it"> <head> <title>Nested sequence</title> </head> <body> <ul n:render="sequence" n:data="control_list"> <li n:pattern="item" n:render="mapping"> <label><n:slot name="ctrl_label"/> <select> <n:attr name="name"><n:slot name="ctrl_name" /></n:attr> <nevow:invisible n:render="sequence" n:data="option_list"> # Yes, I added the tag above because I'm unsure about the way # n:attr is dealt with when it is between the sequence render and # the patterns inside the sequence. This solution removes the # uncertainty. It's generally better to keep the slots filled # directly inside the interested renderer without crossing any other # special renderer tag like this template was doing. <option n:pattern="item" n:render="mapping"> <n:attr name="label"><n:slot name="opt_label" /></n:attr> <n:attr name="value"><n:slot name="opt_value" /></n:attr> </option> </nevow:invisible> </select> </label> </li> </ul> </body> </html> As you can see this solution involves a considerably simpler and more generic Page object that can be reused for a variety of usecases (especially when the data_* method simply runs a query instead of returning an hardcoded result, if the query returns a list of dictionaries already simply putting the query as a Page parameter you'll be able to reuse the whole Page code except the query and then simply change the templates as needed.

Valentino Volonghi aka Dialtone ha scritto:
Sorry and thanks for the response.
[...]
Thanks! Nested slots "was" another of "obscure" points about nevow rendering machinery. However, I still want to do more tests on this.
Ok. Yesterday I read some old messages in the archive, so now I (hopefully) understand better how data directives works. Anyway thanks for your solutions, it is much simpler (I have to modify how the query are done, dicts insteads of list).
Actually option_list is a list of dicts: def data_control_list(self, ctx, data): option_list = [ {'opt_value': '1', 'opt_label': 'uno' }, {'opt_value': '2', 'opt_label': 'due' } ] return [{'ctrl_name': 'a', 'ctrl_label': 'first', 'option_list': option_list }, {'ctrl_name': 'b', 'ctrl_label': 'second', 'option_list': option_list }]
I have tried without nevow:invisible and it seems to work.
Thanks and regards Manlio Perillo

Manlio Perillo ha scritto:
I have Cc to twisted-web, hoping this will help the archiving.
Just for future reference, here is an alternate solution. It contains more code, but it is more database friendly (and I like this - better performance too?). It also contains some correction for the HTML code. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:n="http://nevow.com/ns/nevow/0.1" lang="it" xml:lang="it"> <head> <title>Nested sequence II</title> </head> <body> <form action="post" method="post"> <ul n:render="sequence" n:data="control_list"> <li n:pattern="item" n:render="control"> <label><n:slot name="ctrl_label"/> <select n:render="option_list"> <n:attr name="name"><n:slot name="ctrl_name" /></n:attr> <option n:pattern="item" n:render="option"> <n:attr name="value"><n:slot name="opt_value" /></n:attr> <n:slot name="opt_label" /> </option> </select> </label> </li> </ul> <input type="submit" value="send" /> </form> </body> </html> class Main(rend.Page): addSlash = True docFactory = loaders.xmlfile('nestedsequence.xhtml') def data_control_list(self, ctx, data): return [('a', 'first'), ('b', 'second')] def render_option_list(self, ctx, data): option_list = [('1', 'uno'), ('2', 'due')] tag = ctx.tag pattern = inevow.IQ(ctx).patternGenerator("item") content = [pattern(data=option) for option in option_list] return ctx.tag.clear()[content] def render_control(self, ctx, data): print '*', data ctx.fillSlots('ctrl_label', data[1]) ctx.fillSlots('ctrl_name', data[0]) return ctx.tag def render_option(self, ctx, data): ctx.fillSlots('opt_label', data[1]) ctx.fillSlots('opt_value', data[0]) return ctx Maybe it is not reusable as the previous solution, but it's not a problem. Regards Manlio Perillo

On Sun, 16 Jul 2006 16:18:07 -0200, Manlio Perillo <manlio_perillo@libero.it> wrote: This is off topic here... Twisted-web is the list. Anyway: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:n="http://nevow.com/ns/nevow/0.1" lang="it" xml:lang="it"> <head> <title>Nested sequence</title> </head> <body> <ul n:render="sequence" n:data="control_list"> # Here you call data_control_list. # The result of data_control_list becomes the IContainer implementor # from now on inside this <ul> tag. <li n:pattern="item" n:render="control"> # You call the render_control method on the IRenderer implementor which # is the Main instance this method fills 2 slots ctrl_label and ctrl_name. # every slot inside this <li> tag that has one of those 2 names will be # filled unless an inner tag fills one of those slots with a different # value <label><n:slot name="ctrl_label"/> <select n:render="sequence" n:data="option_list"> # Here you call IContainer(ctx).child('option_list') # IContainer(ctx) is what was returned at the beginning with # data_control_list. # That result of that call is a list and the IContainer adapter # for lists does position lookup. # __getitem__ for lists takes integers and since all the values # coming from the template are strings they have to be converted # to integers. The value coming is 'option_list' and this cannot # be converted to an integer. And thus the error. # The solution? Fairly simple, although it would have been better that, # after I explained how part of the rendering works, YOU tried to find # the solution, anyway I'll explain it :). # # There are 2 solutions (actually more but only 2 that keep using templates): # - Return a list of dictionaries from control_list # - Return a list of 3-tuples from control_list # To avoid changing the template too much let's just see how the # previous line should have been for the second option: # <select n:render="sequence" n:data="2"> # That'it. # def data_control_list(self, ctx, data): # return [('a', 'first', ('1', 'uno')), ('b', 'second', ('2', 'due'))] # Of course I hate this solution because it involes too much code... # you already wrote 3 methods where one would have been largerly enough. <n:attr name="name"><n:slot name="ctrl_name" /></n:attr> <option n:pattern="item" n:render="option"> <n:attr name="label"><n:slot name="opt_label" /></n:attr> <n:attr name="value"><n:slot name="opt_value" /></n:attr> </option> </select> </label> </li> </ul> </body> </html> The best solution is the following (NOT TESTED, but should work): class Main(rend.Page): docFactory = loaders.xmlfile('nested.xhtml') def data_control_list(self, ctx, data): return [{'ctrl_name': 'a', 'ctrl_label': 'first', 'option_list': {'opt_value': '1', 'opt_label': 'uno'} }, {'ctrl_name': 'b', 'ctrl_label': 'second', 'option_list': {'opt_value': '2', 'opt_label': 'second'} }] # nested.xhtml <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:n="http://nevow.com/ns/nevow/0.1" lang="it" xml:lang="it"> <head> <title>Nested sequence</title> </head> <body> <ul n:render="sequence" n:data="control_list"> <li n:pattern="item" n:render="mapping"> <label><n:slot name="ctrl_label"/> <select> <n:attr name="name"><n:slot name="ctrl_name" /></n:attr> <nevow:invisible n:render="sequence" n:data="option_list"> # Yes, I added the tag above because I'm unsure about the way # n:attr is dealt with when it is between the sequence render and # the patterns inside the sequence. This solution removes the # uncertainty. It's generally better to keep the slots filled # directly inside the interested renderer without crossing any other # special renderer tag like this template was doing. <option n:pattern="item" n:render="mapping"> <n:attr name="label"><n:slot name="opt_label" /></n:attr> <n:attr name="value"><n:slot name="opt_value" /></n:attr> </option> </nevow:invisible> </select> </label> </li> </ul> </body> </html> As you can see this solution involves a considerably simpler and more generic Page object that can be reused for a variety of usecases (especially when the data_* method simply runs a query instead of returning an hardcoded result, if the query returns a list of dictionaries already simply putting the query as a Page parameter you'll be able to reuse the whole Page code except the query and then simply change the templates as needed.

Valentino Volonghi aka Dialtone ha scritto:
Sorry and thanks for the response.
[...]
Thanks! Nested slots "was" another of "obscure" points about nevow rendering machinery. However, I still want to do more tests on this.
Ok. Yesterday I read some old messages in the archive, so now I (hopefully) understand better how data directives works. Anyway thanks for your solutions, it is much simpler (I have to modify how the query are done, dicts insteads of list).
Actually option_list is a list of dicts: def data_control_list(self, ctx, data): option_list = [ {'opt_value': '1', 'opt_label': 'uno' }, {'opt_value': '2', 'opt_label': 'due' } ] return [{'ctrl_name': 'a', 'ctrl_label': 'first', 'option_list': option_list }, {'ctrl_name': 'b', 'ctrl_label': 'second', 'option_list': option_list }]
I have tried without nevow:invisible and it seems to work.
Thanks and regards Manlio Perillo

Manlio Perillo ha scritto:
I have Cc to twisted-web, hoping this will help the archiving.
Just for future reference, here is an alternate solution. It contains more code, but it is more database friendly (and I like this - better performance too?). It also contains some correction for the HTML code. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:n="http://nevow.com/ns/nevow/0.1" lang="it" xml:lang="it"> <head> <title>Nested sequence II</title> </head> <body> <form action="post" method="post"> <ul n:render="sequence" n:data="control_list"> <li n:pattern="item" n:render="control"> <label><n:slot name="ctrl_label"/> <select n:render="option_list"> <n:attr name="name"><n:slot name="ctrl_name" /></n:attr> <option n:pattern="item" n:render="option"> <n:attr name="value"><n:slot name="opt_value" /></n:attr> <n:slot name="opt_label" /> </option> </select> </label> </li> </ul> <input type="submit" value="send" /> </form> </body> </html> class Main(rend.Page): addSlash = True docFactory = loaders.xmlfile('nestedsequence.xhtml') def data_control_list(self, ctx, data): return [('a', 'first'), ('b', 'second')] def render_option_list(self, ctx, data): option_list = [('1', 'uno'), ('2', 'due')] tag = ctx.tag pattern = inevow.IQ(ctx).patternGenerator("item") content = [pattern(data=option) for option in option_list] return ctx.tag.clear()[content] def render_control(self, ctx, data): print '*', data ctx.fillSlots('ctrl_label', data[1]) ctx.fillSlots('ctrl_name', data[0]) return ctx.tag def render_option(self, ctx, data): ctx.fillSlots('opt_label', data[1]) ctx.fillSlots('opt_value', data[0]) return ctx Maybe it is not reusable as the previous solution, but it's not a problem. Regards Manlio Perillo
participants (2)
-
Manlio Perillo
-
Valentino Volonghi aka Dialtone