You can achieve that with _group_by_full dictionnary, to add in your osv.osv class.
For example check my sample code:
def _read_group_state_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
stage_obj = self.pool.get('produce.book.stage')
order = stage_obj._order
if read_group_order == 'stage_id desc':
order = "%s desc" % order
# perform search
stage_ids = stage_obj._search(cr, uid, [], order=order, access_rights_uid=access_rights_uid, context=context)
result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
# restore order of the search
result.sort(lambda x, y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
fold = {}
for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context):
fold[stage.id] = stage.fold or False
return result, fold
_group_by_full = {
'stage_id': _read_group_state_ids
}
result is a list of tuples contains (id, name) and fold is a dictionary of pair {id: bool} that if each one be true that column will be fold.