Alan, than you very much! Totally new concept for me, but you got exactly what I needed.
I just fixed couple typos, and copying it here in case someone else needs to resolve a similar problem. rows = db(db.purchase_order.id>0).select(db.purchase_order.po_number, db.item.id, left=db.item.on(db.purchase_order.id==db.item.po_id)) order_numbers = set([row.purchase_order.po_number for row in rows]) new_data = [[number,] + [str(row.item.id) for row in rows if row.purchase_order.po_number == number] for number in order_numbers] # >>> print new_data # result: [['201201009', 291L], ['201201006', 261L], ['201201004', 161L, 171L], ['201201003', 151L, 231L], ['201201002', 111L, 211L], ['201201001', 31L, 41L, 51L, 61L]] new_data.insert(0, ["purchase_order.po_number",]) max_fields = max([len(row) for row in new_data]) csv_output = "" for row in new_data: extra_fields = max_fields - len(row) csv_output += ",".join(row) + "," * extra_fields + "\n" print csv_output #purchase_order.po_number,,,, #201201009,291,,, #201201006,261,,, #201201004,161,171,, #201201003,151,231,, #201201002,111,211,, #201201001,31,41,51,61 Thanks, Adnan