Páginas

lunes, 13 de febrero de 2012

Web2py ejemplo, onvalidation

Ejemplo sencillo donde vamos a validar usando onvalidation 

modelo.py....

db.define_table('tematica',
           Field('nombre'))
db.tematica.nombre.requires = IS_NOT_EMPTY(error_message='validate error')

controladora.py....

def index():

    form = SQLFORM(db.tematica)
   
    def validate(form):
                if form.vars.nombre == 'hola' :
                    form.errors.nombre = 'onvalidation error'
                   
    if form.accepts(request.vars,session,onvalidation=validate): 
       response.flash = 'Todo bien'                        
   
    return dict(form=form)

index.html.....

{{extend 'layout.html'}}
<h4> Escriba hola o déjelo vacio</h4>
{{=form}}





viernes, 14 de octubre de 2011

Web2py ejemplo, trabajo con popup

En este ejemplo vamos a crear una ventana emergente desde la cual insertaremos datos y los mismos se mostrarán en la ventana padre sin refrescar la página, también desde la ventana emergente crearemos una segunda ventana emergente y repetiremos la operación pero esta vez los datos se mostrarán en la primera ventana emergente.

en el modelo.............................

db.define_table('usuario', 
   Field('usuario',))
  
db.define_table('tabla', 
   Field('numero',))


en la controladora.............................

def index():  
    return dict()
   
def formulario():
    form = FORM(INPUT(_name='name',_id='name',requires=IS_NOT_EMPTY()),INPUT(_type='submit'))
   
    close = ' '
   
    if form.accepts(request.vars,session):
        db.usuario.insert(usuario = request.vars.name)       
        close = XML('<script type="text/javascript">$(document).ready(function() { window.opener.cierra() ;});</script>')

    return dict(form=form,close=close)
   
def formulario2():
    form = FORM(INPUT(_name='name',_id='name',requires= IS_INT_IN_RANGE(0, 9)),INPUT(_type='submit'))
   
    close = ' '
   
    if form.accepts(request.vars,session):
        db.tabla.insert(numero = request.vars.name)       
        close = XML('<script type="text/javascript">$(document).ready(function() { window.opener.cierra() ;});</script>')

    return dict(form=form,close=close)    
   
def listausuarios(): 
    listado = []  
    for elem in db(db.usuario).select():      
        listado.append(elem.usuario + "<br/>")
    return listado
   
def listanumeros(): 
    listado = []  
    for elem in db(db.tabla).select():  
        listado.append(elem.numero + "<br/>")
    return listado

en la vista................................................................



index.html.......................

<input type='button' value='añadir' class='link' onclick="abreVentana()" >
<br/><br/>

<div id='target'> </div>

<script>
var miPopup

 $(document).ready(function(){
     cargarLista();

});


function abreVentana(){
    miPopup = window.open("{{=URL(r=request, f='formulario')}}","miwin","width=500,height=400,scrollbars=yes")
    miPopup.focus()
}


function cierra()
{
 cargarLista();
 miPopup.close();
}

function cargarLista(){
$('#target').load('listausuarios');


</script>



formulario.html........................

{{=close}}

{{=form}}<br/>

<div style='margin-left:1%'>
<input type='button' value='añadir' class='link' onclick="abreVentana()" >
<br/><br/>



<div id='target'> </div>

</div>
<script>
var miPopup2

 $(document).ready(function(){
     cargarLista();

});


function abreVentana(){
    miPopup2 = window.open("{{=URL(r=request, f='formulario2')}}","miwin2","width=500,height=400,")
    miPopup2.focus()
}


function cierra()
{
 cargarLista();
 miPopup2.close();
}

function cargarLista(){
$('#target').load('listanumeros');


</script>


formulario2.html........................
{{=close}}
{{=form}}



miércoles, 31 de agosto de 2011

Web2py widget selección en cascada


Referencia: http://www.web2pyslices.com/slices/take_slice/86

En este caso haremos el widget no en los modelos sino como un módulo aparte y lo llamaremos usando import.

en la carpeta Módulos.....

from gluon import *  #  partir de la version 1.96.1
import uuid

class CascadingSelect(object):
    """
       Creates dependent selects based on table relationships.
       Pass the tables in order of least to most specific
       Based on http://web2pyslices.com/main/slices/take_slice/85
    """
    def __init__(self, *tables):
        self.tables = tables
        self.prompt = lambda table:str(table)  
    def widget(self,f,v):
        uid = str(uuid.uuid4())[:8]
        d_id = "cascade-" + uid
        wrapper = TABLE(_id=d_id)
        parent = None; parent_format = None;
        fn =  ''
        vr = 'var dd%s = [];var oi%s = [];\n' % (uid,uid)
        prompt = [self.prompt(table) for table in self.tables]
        vr += 'var pr%s = ["' % uid + '","'.join([str(p) for p in prompt]) + '"];\n'
        f_inp = SQLFORM.widgets.string.widget(f,v)
        f_id = f_inp['_id']
        f_inp['_type'] = "hidden"
        for tc, table in enumerate(self.tables):            
            db = table._db    
            format = table._format           
            options = db(table['id']>0).select()
            id = str(table) + '_' + format[2:-2]            
            opts = [OPTION(format % opt,_value=opt.id,
                                 _parent=opt[str(parent)] if parent else '0') \
                                  for opt in options]
            opts.insert(0, OPTION(prompt[tc],_value=0))
            inp = SELECT(opts ,_parent=str(parent) + \
                                  "_" + str(parent_format),
                                  _id=id,_name=id,
                                  _disabled="disabled" if parent else None)
            wrapper.append(TR(inp))
            next = str(tc + 1)
            vr += 'var p%s = jQuery("#%s #%s"); dd%s.push(p%s);\n' % (tc,d_id,id,uid,tc)           
            vr += 'var i%s = jQuery("option",p%s).clone(); oi%s.push(i%s);\n' % (tc,tc,uid,tc)
            fn_in = 'for (i=%s;i<%s;i+=1){dd%s[i].find("option").remove();'\
                    'dd%s[i].append(\'<option value="0">\' + pr%s[i] + \'</option>\');'\
                    'dd%s[i].attr("disabled","disabled");}\n' % \
                           (next,len(self.tables),uid,uid,uid,uid)
            fn_in +='oi%s[%s].each(function(i){'\
                    'if (jQuery(this).attr("parent") == dd%s[%s].val()){'\
                    'dd%s[%s].append(this);}});' % (uid,next,uid,tc,uid,next)           
            fn_in += 'dd%s[%s].removeAttr("disabled");\n' % (uid,next)
            fn_in += 'jQuery("#%s").val("");' % f_id
            if (tc < len(self.tables)-1):
                fn += 'dd%s[%s].change(function(){%s});\n' % (uid,tc,fn_in)
            else:
                fn_in = 'jQuery("#%s").val(jQuery(this).val());' % f_id
                fn += 'dd%s[%s].change(function(){%s});\n' % (uid,tc,fn_in)
                if v:
                    fn += 'dd%s[%s].val(%s);' % (uid,tc,v)                      
            parent = table
            parent_format = format[2:-2]

        wrapper.append(f_inp)
        wrapper.append(SCRIPT(vr,fn))
        return wrapper

//-------------------------------------------------------------

en el modelo

# recuerde llenar las tablas state y city

db.define_table('state',
                Field('name'),format='%(name)s')
               
db.define_table('city',
                Field('name'),
                Field('state',db.state),format='%(name)s')
               
db.define_table('location',
                Field('name'),               
                Field('location',db.city))

//-----------------------------------------------------------

en la controladora

def index():
    from mymodule import CascadingSelect
   
    cascade = CascadingSelect(db.state,db.city)
    db.location.location.widget = cascade.widget
   
    form = SQLFORM(db.location)
    if form.accepts(request.vars,session):
        response.flash= 'localizacion confirmada'
    return dict(form=form)
   
def mostrar():
    lista = []
    listalocation = db(db.location.id>0).select()
    listacity = db(db.city.id>0).select()
    listastate = db(db.state.id>0).select()
   
    for elem in listalocation :
        for elem2 in listacity:
           for elem3 in listastate:
            if(elem.location == elem2.id) and (elem2.state == elem3.id):
                lista.append([elem.name,elem2.name,elem3.name])
   
    return dict(lista=lista)


//---------------------------------------------------

en las vistas

index.html.........

[{{=A('Mostrar',_href=URL(r=request,c='default',f='mostrar'))}}]
{{=form}}

mostrar.html............

<table>
<tr>
{{for elem in lista:}}
    {{for row in elem:}}
    <td> {{=row}}</td>
{{pass}}
</tr>
{{pass}}
</table>

martes, 28 de junio de 2011

Web2py ejemplo sencillo donde vincularemos python con javascript

En este pequeño ejemplo vamos a mostrar como pasar variables python hacia el código javascript, fijaos bien que las variables python dentro del javascript tienen '   ' ,comillas simples, ejemplo '{{variable}}'.

en la controladora.............................

def index():  

    var1 = 'hola'  # la pasaremos como parametro de la función mostrar()
    var2 = 'hola2'  # la pasaremos directamente dentro de dicha función  
 
    return dict(var1=var1, var2=var2)

en la vista...............................................................

index.html

{{extend 'layout.html'}}

{{=var1}}<br/>
{{=var2}}<br/>
{{lista = ['hola','hola2']}}

<input type="button" value='prueba' onclick="mostrar('{{=var1}}')"/>

<script type="text/javascript">

function mostrar(variable)

    var Lista = []
    alert(variable);
    var mivarjs='{{=var2}}';
    alert(mivarjs);
    {{for i in lista:}}
        Lista.push('{{=i}}');
     {{pass}} 
     alert(Lista);
  
}
</script>



miércoles, 12 de enero de 2011

Web2py ejemplo ajax upload hecho por michele.comitini

Esta ejemplo permite a los usuarios (sin autenticación) asociar imágenes a los usuarios registrados (primero tenes que registrar algunos usuarios). La lista es actualizada sin recargar toda la página.

Referencia: http://www.web2pyslices.com/main/slices/take_slice/114

Primero crear dentro de la carpeta static/js el archivo ajaxupload.js

Despues descargar este plugin:
Copiar y pegar contenido del plugin en el archivo ajaxupload.js

en el modelo................

db.define_table('user_pictures',
                Field('user', db.auth_user, unique=True, represent=lambda id: '%s %s ' %( db.auth_user(id).first_name, db.auth_user(id).last_name)),
                Field('picture', 'upload', requires=IS_IMAGE()))
               
db.user_pictures.user.requires=IS_IN_DB(db,'auth_user.id', '%(first_name)s %(last_name)s', zero=T('Escoge uno'))


default.py.............................................

def index():
        return dict()
   
def user_picture_list():
    rows = db(db.user_pictures.id>0).select()
    return dict(rows=rows)

def user_picture_form():
    form = SQLFORM(db.user_pictures)
    if form.accepts(request.vars):
       response.flash='Thanks for filling the form'
       return XML('ajax("%s",[],"pic_list");' % URL('user_picture_list.load'))
    elif form.errors:
        response.flash='Fill the form correctly'
    else:
        response.flash='Please, fill the form'

    return dict(form=form)


en las vistas................................................


default/index.html
{{response.files.append(URL(r=request,c='static',f='js/ajaxupload.js'))}}
{{extend 'layout.html'}}

<div class='ez-wr'>

    <h2>Esta lista crecera a medida que se carguen las imágenes</h2>

  <div class='ez-box' id='pic_list'>
    {{=LOAD('default', 'user_picture_list.load', ajax=False)}}
  </div>
</div>
<div class='ez-wr'>
  <p>
    Cargue una nueva imagen  </p>
  <div class='ez-box'>
    {{=LOAD('default','user_picture_form.load', ajax=False)}}
  </div>
</div>

default/user_picture_form.load     //fijaos que la terminacion es (.load) no (.html) y tampoco se le pone {{extend 'layout.html'}}


{{=form}}
<script type='text/javascript'>
  //<!--
    jQuery('document').ready(function() {
        jQuery('form').ajaxForm({dataType: 'script',
                 url: "{{=URL('user_picture_form')}}"
                 });
});
                     //-->
</script>

default/user_picture_list.load    //fijaos que la terminacion es (.load) no (.html) y tampoco se le pone {{extend 'layout.html'}}


<div class='ez-wr'>
     {{for r in rows:}}
     <div class="ez-wr">
       <div class="ez-fl ez-negmr ez-50">
     {{user = db.auth_user(r.user)}}
     {{="%s %s" % (user.first_name,user.last_name)}}
       </div>
       <div class="ez-last ez-oh">
     {{=IMG(_src=URL(f='download',args=[r.picture]))}}
       </div>
     </div>
     {{pass}}
   </div>

lunes, 10 de enero de 2011

Web2py ejemplo sobre selección dinámica y auto-completar.

En este ejemplo vamos a utilizar los ayudantes FORM, SELECT, OPTION etc... donde mostraremos de forma dinámica los meses en un combo-box, también haremos una búsqueda de esos mismo meses a medida que vamos escribiendo.

Más Información: http://www.latinuxpress.com/books/drafts/web2py/caps/capz10.html#auto-completar

default.py.....................................

def index():
    lista = ['Enero' , 'Febrero' , 'Marzo' , 'Abril' , 'Mayo' , 'Junio', 'Julio','Agosto' , 'Septiembre' , 'Octubre' , 'Noviembre' , 'Diciembre']

    form=FORM(LABEL('Selecione un mes: '),SELECT([OPTION(texto, _value=texto) for texto in lista],_name='combo'),BR(),LABEL('Escribir el nombre de un mes:'),INPUT(_name='month',_id='month'))

    return dict(lista=lista,form=form)

def month_selector():

    months = ['Enero' , 'Febrero' , 'Marzo' , 'Abril' , 'Mayo' , 'Junio' , 'Julio' , 'Agosto' , 'Septiembre' , 'Octubre' , 'Noviembre' , 'Diciembre']

    month_start = request.vars.month.capitalize()
    selected = [m for m in months if m.startswith(month_start)]
    return DIV(*[DIV(k,
                                 _onclick="jQuery('#month').val('%s')" % k,
                                 _onmouseover="this.style.backgroundColor='PaleGreen'",
                                 _onmouseout="this.style.backgroundColor='white'",
                                 _onmouseup="jQuery('.suggestions').hide()",
                                 ) for k in selected])

default/index.html.............................
{{extend 'layout.html'}}


<style>
#suggestions { position: relative; }
.suggestions { background: white;  }
.suggestions DIV { padding: 2px 4px 2px 4px; }
</style>

{{=form}}


<div id='suggestions'> </div>

<script>
jQuery("#month").keyup(function(){
      ajax('month_selector', ['month'], 'suggestions')});
</script>



viernes, 31 de diciembre de 2010

Web2py ejemplo de paginación

Vamos a paginar una tabla en la vista cada 10 elementos.

en el modelo...................

#recuerde llenar la tabla

db.define_table('persona',
     Field('fecha','datetime'),
     Field('nombre'),
     Field('apellido'),
     Field('profesion'))

en la controladora.............................

def index():
   if len(request.args):
       page=int(request.args[0])
   else:
       page=0

   items_per_page=10   # cantidad de elementos por los cuales hacer la paginación

   limitby=(page*items_per_page,(page+1)*items_per_page+1)    
     
   lista = db(db.persona.id > 0).select(orderby = ~db.persona.fecha,limitby=limitby)
   return dict(lista=lista,page=page,items_per_page=items_per_page)

en la vista...............................................................

index.html

{{extend 'layout.html'}}
<table>
<tr>

<td>Fecha </td>
<td>Nombre</td>
<td>Apellido</td>
<td>Profesion</td>

</tr>
<tr>
{{for i,row in enumerate(lista):}}
{{if i==items_per_page: break}}
<td> {{=row.fecha}}</td>
<td> {{=row.nombre}}</td>  
<td> {{=row.apellido}}</td> 
<td> {{=row.profesion}}</td>

</tr> 
{{pass}}
</table>

<br/>
{{if page:}}
<a href="{{=URL(args=[page-1])}}">anterior</a>
{{pass}}


{{if len(lista)>items_per_page:}}
<a href="{{=URL(args=[page+1])}}">siguiente</a>
{{pass}}

miércoles, 29 de diciembre de 2010

Web2py un ejemplo donde usaremos la función LOAD



Vamos a tratar de comentar los comentarios. Tendremos muchos comentarios padres y cada comentario padre tendrá muchos subcomentarios hijos. Cuando se crea un comentario padre automáticamente se crea un formulario para los subcomentarios, cada hijo sabrá quien es su padre y todo esto ocurrirá en una misma página (algo parecido a lo que hace el facebook).

Este ejemplo funciona satisfactoriamente gracias a la cooperación de Alan Etkin:

en el modelo..........................................

db.define_table('comment',
   Field('body',label='Comentario'))
  
db.define_table('comment2',
   Field('body',label='Comentario'),
   Field('idcomentario',db.comment))  
  
db.comment.body.requires=IS_NOT_EMPTY()
db.comment2.body.requires=IS_NOT_EMPTY()
db.comment2.idcomentario.writable = db.comment2.idcomentario.readable = False



en la controladora........................................

def index():  

    form = SQLFORM(db.comment,fields=['body'])   
    if form.accepts(request.vars,session):
        response.flash='su comentario ha sido posteado'
        
        
    lista = db(db.comment.id>0).select(orderby = ~db.comment.id)
    
    return dict(form=form,lista=lista)
               

def post():   
    id1=request.args[0]
    comentario=db(db.comment.id==request.args[0]).select()[0]
    form = SQLFORM(db.comment2,fields=['body'])
    form.vars.idcomentario=comentario.id
 
    if form.accepts(request.vars,session):
         response.flash='su comentario ha sido posteado'
    elif request.post_vars.has_key('body'):     # Alan Etkin aporte
          if len(request.post_vars['body']) >0:
             response.flash='su comentario ha sido posteado'
             db.comment2.insert(body = request.post_vars['body'],idcomentario = id1)
            
   
    
    comments=db(db.comment2.idcomentario==comentario.id).select()

    return dict(form=form,comments=comments)


en la vista.....................................................

default/index.html

{{extend 'layout.html'}}

<h2>Postea un comentario:</h2>
<div>
{{=form}}
</div>
{{if len(lista):}}
{{for elem in lista:}}
     <h3>{{=elem.body}}</h3>
    <div>{{=LOAD('default','post.load',args=[elem.id],ajax=True)}}</div>
 {{pass}}
{{else:}}
    <h2>No hay comentarios</h2>
{{pass}}



default/post.load  // fijaos que la terminacion es (.load) no (.html) y tampoco se le pone {{extend 'layout.html'}}


{{if len(comments):}}
{{for comment in comments:}}
   <h5>{{=comment.body}}</h5>
{{pass}}
{{else:}}
{{pass}}

{{=form}}

Web2py un ejemplo interesante hecho por mdipierro

Hay veces en las cuales usted tiene dos tablas (ejemplo "cliente", 'dirección') que están unidos entre sí por una referencia y desea crear un único formulario que permite introducir información acerca de un cliente y su dirección por defecto. Asi es como se hace.

Referencia: http://www.web2pyslices.com/main/slices/take_slice/102

en el modelo

db.define_table('client',
     Field('name'))
db.define_table('address',
    Field('client',db.client,writable=False,readable=False),
    Field('street'),Field('city'))

en la controladora

def register():
    form=SQLFORM.factory(db.client,db.address)
    if form.accepts(request.vars):
        id = db.client.insert(**db.client._filter_fields(form.vars))
        form.vars.client=id
        id = db.address.insert(**db.address._filter_fields(form.vars))
        response.flash='Thanks for filling the form'
    return dict(form=form)

Personalizando el menú de la app Welcome en Web2Py - Parte 1

Una de las cosas más interesantes de web2py es su gran versatilidad y sencillez, esto reduce sensiblemente la curva de aprendizaje en comparación con otros frameworks.
En este post -dividido en dos partes- aprenderemos a personalizar el menú por defecto que viene en la app "Welcome" de Web2Py.
En esta primera parte veremos como programar diferentes opciones del menú, en la segunda veremos como modificar la apariencia del menu en la vista.
El fichero en cuestión que modificarás será el siguiente: tu-app/models/menu.py, el cual luce de la siguiente manera:
# -*- coding: utf-8 -*-

#########################################################################
## Customize your APP title, subtitle and menus here
#########################################################################

response.title = request.application
response.subtitle = T('customize me!')
response.meta.author = 'you'
response.meta.description = 'describe your app'
response.meta.keywords = 'bla bla bla'

##########################################
## this is the main application menu
## add/remove items as required
##########################################

response.menu = [
(T('Index'), False, URL(request.application,'default','index'), [])
]

##########################################
## this is here to provide shortcuts
## during development. remove in production
##
## mind that plugins may also affect menu
##########################################

response.menu+=[
(T('Edit'), False, URL('admin', 'default', 'design/%s' % request.application),
[
(T('Controller'), False,
URL('admin', 'default', 'edit/%s/controllers/%s.py' \
% (request.application,request.controller=='appadmin' and
'default' or request.controller))),
(T('View'), False,
URL('admin', 'default', 'edit/%s/views/%s' \
% (request.application,response.view))),
(T('Layout'), False,
URL('admin', 'default', 'edit/%s/views/layout.html' \
% request.application)),
(T('Stylesheet'), False,
URL('admin', 'default', 'edit/%s/static/base.css' \
% request.application)),
(T('DB Model'), False,
URL('admin', 'default', 'edit/%s/models/db.py' \
% request.application)),
(T('Menu Model'), False,
URL('admin', 'default', 'edit/%s/models/menu.py' \
% request.application)),
(T('Database'), False,
URL(request.application, 'appadmin', 'index')),
]
),
]

La línea que nos interesa es la siguiente:

response.menu = [
(T('Index'), False, URL(request.application,'default','index'), [])
]
Aqui tenemos el primer item del menú que viene por defecto y vamos a pasar a describir su contenido. "Index" es lo que aparece visiblemente en el menú. El parámetro "False" es un misterio, aunque seguramente mediante la práctica se puede aprender su utilidad (porque no encontré documentación al respecto). Luego el siguinte parámetro "URL" muy fácil de entender: pasamos la aplicación en cuestion (si dejamos vacios se usa la app presente), el controlador (si dejamos vacio usará "default.py")y la función en cuestión, en este caso index.
Como el primer item de nuestro menú es index, no existen submenús, pero qué si queremos tener un submenú aquí? Muy fácil, creamos una nueva tupla dentro de los corchetes que quedaron vacios, como un parámetro más y podríamos continuar así recursivamente.

Sin embargo tenemos más código, que significa? Significa que cada vez que agregamos un item al menú debemos agregalo de la siguiente manera:

response.menu+=[...] #Entre corchetes va el siguiente menú.

En este caso, el nuevo item es "Edit":

response.menu+=[
(T('Edit'), False, URL('admin', 'default', 'design/%s' % request.application),...

en "URL" debemos seguir los pasos descriptos anteriormente y borrar lo que aparece aqui.
Lo mismo debemos hacer con los siguientes subitems que vienen dentro de la linea. Con simple copie y pegue y sustituyendo nombres y controladores podemos armar sin mas complicaciones nuestros menúes. Es más, con un poco de práctica y experiencia podrías crear varios menúes diferentes. Pero eso es letra para otro post.

Practiquen y prueben. Hagan sus preguntas en los comentarios si algo no quedó claro (seguramente :P ...)y trataremos de responder lo antes posible.

martes, 28 de diciembre de 2010

Web2py un ejemplo donde usaremos la función ajax

Vamos a tratar de modelar lo que hace el gmail a la hora de eliminar los correos en la vista estándar (sin refrescar la pagina), esto se logra usando ajax. La función ajax(url,[name],target)  utilizada en este ejemplo viene por defecto con el Web2py y está basada en jQuery $.ajax.

en el modelo

db.py:

# recuerde llenar la tabla

db.define_table('gmail',   
     Field('correo'))

..................................................................
en la vista

index.html:

{{extend 'layout.html'}}
<INPUT type="button" value="suprimir" onclick="ajax('{{=URL('data')}}',['hijos'],'target');"/><br/><br/>

<span id='target'>
{{if len(lista):}}
{{for row in lista:}}
     {{=INPUT(_type='checkbox', _name='hijos', _value=row.id)}}
    <span style='margin-left:0%'>{{=row.correo}}</span><br/>
     {{pass}}
{{else:}}
    No hay datos
{{pass}}
</span>

.....................................................................................
en la controladora

default.py

def index():
   
    lista = db(db.gmail).select()
   
    return dict(lista=lista)
   
def data():   
    listado = []      
    if request.vars.hijos:
       listacheck = request.vars.hijos
       if not isinstance(listacheck,list):
           listacheck=[listacheck,]         
                 
       for elem in listacheck:                    
             db(db.gmail.id== elem).delete()
            
       if(db(db.gmail.id > 0).count()==0):
           return 'No hay datos'
       else:
          pass   
    
       for row in db(db.gmail).select():
          listado.append(INPUT(_type='checkbox',_name='hijos',_value=row.id))
          listado.append(SPAN(row.correo,_style='margin-left:0.5%'))         
          listado.append(BR())
         
       a = DIV(listado)

    return a

...............................................................................