generating tables using mako templates
May 22, 2009 4:01 AM   Subscribe

Using a Mako template and CSS, I need to generate a m x n grid display from a variable length list of elements.

For simplicity's sake, let's say what I want is a 2 x n table, but I'm fine with unordered lists as long as they appear on screen aligned as if they were in rows and columns.

What does the logic look like in mako to create such a structure? I'm having trouble figuring out when to start a new row or close and old one, while still keeping the template simple and readable.

Please spell things out and use small words as I'm new to both Mako and CSS (but somewhat conversant in Python), and keep me from the temptation to hard-code a solution for a fixed length list of elements.
posted by zippy to Computers & Internet (6 answers total)
 
The two cases are actually different. If you want unordered lists, you'll split the input list into columns. But if you want a table-based grid, you'll split it into rows.

Either way, I would split the list into rows or columns first, then use a simple nested for loop over the resulting data structure.

Assuming this is purely a "display" thing (i.e. not part of domain logic at all) and that you might want to alter the number of columns just by editing the template, I'd define a function to do the list-splitting and then call it from your template. You can import modules in code blocks inside templates, something like:
<>
Then, later:
% for column in split_into_cols(my_list, COLS):
    <ul class="column">
    % for item in column:
        display item
    % endfor
    </ul>
% endfor
If you want to use unordered lists, you'll furthermore need to play around with the CSS till you get the look you want. The general way to do this would be to give the lists float: left and a width in either pixels or percent, and put a div with clear:left at the bottom and/or enclose them in a container with float: left; width: 100%;. Hard to say without knowing what "look" you want. Since you said "grid" in your post, you might prefer a table.
posted by pengale at 6:28 AM on May 22, 2009


Metafilter ate my first code block. Worked in live preview!
<%!
from foo import split_into_cols
COLS = 2
%>

posted by pengale at 6:29 AM on May 22, 2009


Don't be afraid of using HTML tables. You are, after all, displaying a table of data.
posted by zsazsa at 9:23 AM on May 22, 2009


Using only CSS it is pretty easy to get this style output from a single list:
1,2,3
4,5,6
7,8,9

And there are a variety of client-side tricks to get the more common:
1,4,7
2,5,8
3,6,9

You can see some options explored at A List Apart: CSS Swag: Multi-Column Lists

The quick test to decide if data is tabular is think up names for the rows and columns. If you have to use artificial labels like "first third of the names" then you probably have a list not a table.
posted by samsm at 9:54 AM on May 22, 2009


Response by poster: Could you give me an example of a list and the output you expect?

Sure.

list = [a b c d e f]

Display:

a b
c d
e f

The code I currently have shows the items in a single column display. I just want to show more than one item per row.

I do not have the sort of information that requires row headings and column headings - it's not a true table - I just want to lay out the data on screen in a denser way than a straight unordered list, while maintaining some visual order in the layout.

The actual contents of each cell (a, b, etc.) are a thumbnail image and a line of text. In the output I would like to maintain visual balance for each cell. Specifically I want to constrict how much horizontal space the text can occupy, so that it flows next to the thumbnail.

Here's my existing code:

      <table cellspacing="0" cellpadding="0" border="0">
       <tr class="b">

        % for result in results:
          <td class="b">
            <td>
              <%call expr="self.link(result)">
                <img src="${result['thumbnail'] | h}" border="0">
              </%call>
            </td>

            <td>
              <%call expr="self.link(result)">${h.truncate(result['name'], length=25) | h}</%call>
            </td>
          </td>
        % endfor
      </tr>
      </table>

posted by zippy at 11:51 AM on May 22, 2009


Best answer: I initially misunderstood your intention: I thought you wanted them grouped by columns rather than by rows.

If you want the individual items to be of fixed size, you could use CSS and do this without a table. Just give each item display: block; float: left; overflow:hidden; and a fixed width and height, (or width: 49.5% for two columns) and put them into a container that is sized so they wrap the way you want. This could cause problems if your text overflows and is truncated, but I see you're already limiting the length of the text.

If you want to split the list in Python, a list comprehension would work fine.
my_list = range(21) # sample data
COLS = 2
num_rows = (len(my_list) + COLS - 1) / COLS
out_list = [my_list[x * COLS:(x + 1) * COLS] for x in xrange(num_rows)]
Then use nested for loops (the output is a list of lists that in this case goes [[1,2],[3,4]...]) and structure the HTML however you want.
posted by pengale at 12:55 PM on May 22, 2009


« Older My first proper career choice...   |   His Two Friends Were soooo Fine Newer »
This thread is closed to new comments.