[Twisted-Python] again on nested sequence

Valentino Volonghi aka Dialtone dialtone at divmod.com
Sun Jul 16 16:23:30 MDT 2006



On Sun, 16 Jul 2006 16:18:07 -0200, Manlio Perillo <manlio_perillo at 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.




More information about the Twisted-Python mailing list