Las Vistas

web2py usa Python para sus modelos, controladores y vistas, aunque usa una sintáxis de Python ligeramente modificada en las vistas que permite un código mas leíble sin poner restricciones en el uso adecuado de Python.

El propósito de una vista es incrustar código (Python) en un documento HTML. En general esto trae algunos problemas:

  • ¿Cómo debe el código embebido ser escapado?
  • ¿Las reglas de sangrado son las de Python o las de HTML?

web2py usa {{ ... }} para escapar código Python embebido en HTML. La ventaja de usar llaves en vez de paréntesis rectos es que esto es transparente a todos los editores comunes de HTML. Esto le permite al desarrollador usar esos editores para crear vistas web2py.

Como el desarrollador está incrustando código Python en HTML, el documento debe tener las sangrías de acuerdo a las reglas de HTML, pero no las de Python. Por lo tanto, permitimos Python sin sangrías dentro de las etiquetas {{ ... }} . Como Python usa normalmente la sangría para delimitar bloques de código, se necesita una forma diferente de hacerlo aquí, esta es la razón por la que el lenguaje de plantillas web2py hace uso de la palabra clave Python pass.

Un bloque de código comienza con una línea que finaliza con dos puntos y termina con una línea que comienza con pass. La palabra clave pass no se necesita cuando el final del bloque es obvio desde el contexto dado.

He aquí un ejemplo:

{{
if i == 0:
response.write('i is 0')
else:
response.write('i is not 0')
pass
}}

Se observa que pass es una palabra clave Python, no una palabra clave web2py. Algunos editores de Python, tal como Emacs, usan la palabra clave pass para darle significado a la división de bloques y usarla para re-sangrar el código automáticamente.

El lenguaje de plantillas web2py hace exactamente lo mismo. Cuando encuentra algo como:

1
2
3
<html><body>
{{for x in range(10):}}{{=x}}hello<br />{{pass}}
</body></html>

Lo traduce en un programa:

1
2
3
4
5
response.write("""<html><body>""", escape=False)
for x in range(10):
    response.write(x)
    response.write("""hello<br />""", escape=False)
response.write("""</body></html>""", escape=False)

response.write escribe al response.body.

Cuando hay un error en una vista web2py, el reporte de error muestra el código generado de la vista, no la vista como fué actualmente escrita por el desarrollador. Esto ayuda al desarrollador a corregir el código resaltando el código que realmente se ejecuta (lo cual es algo que puede ser corregido con un editor HTML o el inspector DOM del browser).

Note también que:

1
{{=x}}

genera

1
response.write(x)

Las variables inyectadas en el HTML de esta forma se escapan por defecto. El escape se ignora si x es un objeto XML, aún si escape tiene asignado True.

Aquí un ejemplo que introduce el ayudante H1:

1
{{=H1(i)}}

lo cual se traduce a:

1
response.write(H1(i))

Basado en la evaluación, el objeto H1 y sus componentes son serializados recursivamente, escapados y escritos al cuerpo de respuesta. Las etiquetas generadas por H1 y el código HTML interno no se escapan. Este mecanismo garantiza que todo el texto – y solo el texto – desplegado en la página web siempre se escapa, lo cual previene las vulnerabilidades XSS. Al mismo tiempo el código es simple y fácil de corregir.

El método response.write(obj, escape=True) toma dos argumentos, el objeto a escribir y si tiene que ser escapado o no (es True por defecto). Si obj tiene un método .xml(), este se llama y el resultado es escrito al cuerpo de respuesta (el argumento escape se ignora). En caso contrario usa el método del objeto __str__ para serializarlo y, si el argumento escape es True, lo escapa. Todos los objetos ayudantes ya integrados (H1 en el ejemplo) son objetos que saben como serializarse a si mismos a través del método .xml().

Todo esto se hace de manera transparente. Nunca se necesita (y nunca se debe) llamar al método response.write de manera explícita.

Sintáxis Básica

El lenguaje de plantillas web2py soporta todas las estructuras de control de Python. Aquí se proveen algunos ejemplos de cada una de ellas. Éstas pueden ser anidadas de acuerdo a las prácticas usuales de programación.

for...in

En el lenguaje de plantillas web2py se puede iterar sobre cualquier objeto que lo permita:

1
2
3
4
{{items = ['a', 'b', 'c']}}
<ul>
{{for item in items:}}<li>{{=item}}</li>{{pass}}
</ul>

Lo que produce:

1
2
3
4
5
<ul>
<li>a</li>
<li>b</li>
<li>c</li>
</ul>

Aqui item es cualquier objeto sobre el que se puede hacer iteraciones, tal como una lista Python, una tupla Python o un objeto Rows, o cualquier objeto que es implementado como que puede ser iterado. Los elementos mostrados son serializados primero y luego se escapan.

while

Se puede iterar usando la palabra clave while:

1
2
3
4
{{k = 3}}
<ul>
{{while k > 0:}}<li>{{=k}}{{k = k - 1}}</li>{{pass}}
</ul>

Lo que produce:

1
2
3
4
5
<ul>
<li>3</li>
<li>2</li>
<li>1</li>
</ul>

if...elif...else

If (si condicional) elif (si no, si condicional) else (si no)

Se pueden usar claúsulas condicionales:

1
2
3
4
5
6
7
8
{{
import random
k = random.randint(0, 100)
}}
<h2>
{{=k}}
{{if k % 2:}}is odd{{else:}}is even{{pass}}
</h2>

Lo que produce:

1
2
3
<h2>
45 is odd
</h2>

Como es obvio que else cierra el primer bloque if, no hay necesidad de la declaración pass, y usar una sería incorrecto. Sin embargo, explícitamente debe cerrarse el bloque else con un pass.

Se recuerda que en Python el “else if”se escribe elif como en el siguiente ejemplo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{{
import random
k = random.randint(0, 100)
}}
<h2>
{{=k}}
{{if k % 4 == 0:}}is divisible by 4
{{elif k % 2 == 0:}}is even
{{else:}}is odd
{{pass}}
</h2>

Esto produce:

1
2
3
<h2>
64 is divisible by 4
</h2>

try...except...else...finally

Try (tratar) except (excepto) else (si no) finally (finalmente)

También es posible usar declaraciones try...except en vistas, pero con un pequeño detalle. Considere el siguiente ejemplo:

1
2
3
4
5
6
7
8
9
{{try:}}
Hello {{= 1 / 0}}
{{except:}}
division by zero
{{else:}}
no division by zero
{{finally}}
<br />
{{pass}}

Este produce la siguiente salida:

1
2
3
Hello
division by zero
<br />

Este ejemplo ilustra que toda la salida generada antes de que una excepción ocurra se procesa (incluyendo la salida que precede la excepción) dentro del bloque. “Hello” se escribe porque esto precede la excepción.

def...return

Def (definir) return (devolver)

El lenguaje de plantillas web2py permite al desarrollador definir e implementar funciones que pueden devolver cualquier objeto Python o una cadena de texto o de html. Aquí consideramos dos ejemplos:

1
2
3
4
{{def itemize1(link): return LI(A(link, _href="http://" + link))}}
<ul>
{{=itemize1('www.google.com')}}
</ul>

Lo que produce la siguiente salida:

1
2
3
<ul>
<li><a href="http:/www.google.com">www.google.com</a></li>
</ul>

La función itemize1 devuelve un objecto ayudante que se inserta en el sitio desde donde la función se llama. Considere el siguiente código:

1
2
3
4
5
6
{{def itemize2(link):}}
<li><a href="http://{{=link}}">{{=link}}</a></li>
{{return}}
<ul>
{{itemize2('www.google.com')}}
</ul>

Este produce exactamente la misma salida que el de arriba. En este caso la función itemize2 representa una pieza de HTML que va a reemplazar la etiqueta web2py donde se llama la función. Note que no hay ‘=’ al principio de la llamada a itemize2, dado que la función no retorna el texto, sino que lo escribe directamente en la respuesta.

Hay un detalle: las funciones que se definen dentro de una vista deben terminar con una declaración return, o la sangría automática fallará.

Ayudantes HTML

Considere el siguiente código en una vista:

1
{{=DIV('this', 'is', 'a', 'test', _id='123', _class='myclass')}}

El cual es procesado como:

1
<div id="123" class="myclass">thisisatest</div>

DIV es una clase ayudante, por ejemplo, algo que puede ser usado para construir HTML programáticamente. Este corresponde a la etiqueta HTML <div>.

Argumentos posicionales se interpretan como objetos contenidos entre las etiquetas que abren y cierran. Argumentos nombrados que comienzan con un subrayado se interpertan como atributos de etiquetas HTML (sin el subrayado). Algunos ayudantes también tienen argumentos con nombre que no comienzan con el subrayado; estos argumentos son específicos de etiquetas.

El siguiente conjunto de ayudantes:

A, B, BEAUTIFY, BODY, BR, CENTER, CODE, DIV, EM, EMBED, FIELDSET, FORM, H1, H2, H3, H4, H5, H6, HEAD, HR, HTML, I, IFRAME, IMG, INPUT, LABEL, LEGEND, LI, LINK, OL, UL, MARKMIN, MENU, META, OBJECT, ON, OPTION, P, PRE, SCRIPT, OPTGROUP, SELECT, SPAN, STYLE, TABLE, TAG, TD, TEXTAREA, TH, THEAD, TBODY, TFOOT, TITLE, TR, TT, URL, XHTML, XML, xmlescape, embed64

pueden ser usados para construir expresiones complejas que puede ser serializads a XML 49 50. Por ejemplo:

1
{{=DIV(B(I("hello ", "<world>"))), _class="myclass")}}

Se procesa como:

1
<div class="myclass"><b><i>hello <world></i></b></div>

El mecanismo de ayudantes en web2py es más que un sistema que genera HTML sin concatenamiento de cadenas. Este provee una representación del lado del servidor del Modelo de Documento Objeto (DOM).

Los componentes de los objetos pueden ser referenciados de acuerdo a su posición, y los ayudantes actúan como listas con respecto a sus componentes:

1
2
3
4
5
6
7
8
>>> a = DIV(SPAN('a', 'b'), 'c')
>>> print a
<div><span>ab</span>c</div>
>>> del a[1]
>>> a.append(B('x'))
>>> a[0][0] = 'y'
>>> print a
<div><span>yb</span><b>x</b></div>

Los atributos de los ayudantes pueden ser referenciados por nombre, y los ayudantes actúan como diccionarios con respecto a sus atributos:

1
2
3
4
5
>>> a = DIV(SPAN('a', 'b'), 'c')
>>> a['_class'] = 's'
>>> a[0]['_class'] = 't'
>>> print a
<div class="s"><span class="t">ab</span>c</div>

XML

XML es un objeto usado para encapsular texto que no debe ser escapado. El texto puede o no tener XML válido. Por ejemplo, este puede contenet JavaScript.

El texto en este ejemplo se escapa:

1
2
>>> print DIV("<b>hello</b>")
&lt;b&gt;hello&lt;/b&gt;

Si se usa XML se puede prevenir el escape:

1
2
>>> print DIV(XML("<b>hello</b>"))
<b>hello</b>

Algunas veces se quiere mostrar HTML guardado en una variable, pero el HTML puede contener etiquetas inseguras, tal como scripts:

1
2
>>> print XML('<script>alert("unsafe!")</script>')
<script>alert("unsafe!")</script>

Entrada ejecutable sin escapar tal como ésta ( por ejemplo, ingresada en el cuerpo de un comentario en un blog) es insegura, porque puede ser usada para generar ataques Cross Site Scripting (XSS) contra otros visitantes de la página.

El ayudante web2py XML puede desinfectar nuestro texto para preveer inyecciones y escapar todas las etiquetas excepto aquellas que se permitan de manera explícita. Aquí un ejemplo:

1
2
>>> print XML('<script>alert("unsafe!")</script>', sanitize=True)
&lt;script&gt;alert(&quot;unsafe!&quot;)&lt;/script&gt;

Los constructores XML, por defecto, consideran el contenido de algunas etiquetas y algunos de sus atributos seguros. Se pueden anular las imposiciones por defecto usando los argumentos opcionales permitted_tags y allowed_attributes. Aquí están los valores por defecto de los argumentos opcionales del ayudante XML.

1
2
3
4
5
XML(text, sanitize=False,
    permitted_tags=['a', 'b', 'blockquote', 'br/', 'i', 'li',
       'ol', 'ul', 'p', 'cite', 'code', 'pre', 'img/'],
    allowed_attributes={'a':['href', 'title'],
       'img':['src', 'alt'], 'blockquote':['type']})

Ayudantes integrados

A

Este ayudante se usa para construir enlaces.

1
2
3
>>> print A('<click>', XML('<b>me</b>'),
            _href='http://www.web2py.com')
<a href='http://www.web2py.com'>&lt;click&gt;<b>me/b></a>

Toma un argumento especial llamado cid. Trabaja así:

1
2
<div id="myid"></div>
{{=A('linked page',_href='http://example.com',cid='myid')}}

Y un clic en los enlaces expone el contenido a ser cargado en el div. Esto requiere {{include ‘web2py_ajax.html’}} en el encabezado del diseño.

Aplicaciones del cid se discutirán en mas detalle en el Capítulo 13, en el contexto de componentes.

B

Este ayudante hace que el contenido se resalte.

1
2
>>> print B('<hello>', XML('<i>world</i>'), _class='test', _id=0)
<b id="0" class="test">&lt;hello&gt;<i>world</i></b>

BODY

Este ayudante hace el cuerpo de una página

1
2
>>> print BODY('<hello>', XML('<b>world</b>'), _bgcolor='red')
<body bgcolor="red">&lt;hello&gt;<b>world</b></body>

CENTER

Este ayudante centra el contenido.

1
2
3
>>> print CENTER('<hello>', XML('<b>world</b>'),
>>>              _class='test', _id=0)
<center id="0" class="test">&lt;hello&gt;<b>world</b></center>

CODE

Este ayudante resalta la sintáxis para código Python, C, C++, HTML y web2py, y es preferible a PRE para listar códigos. CODE también tiene la habilidad de crear enlaces para la documentación web2py y API.

He aquí un ejemplo del resaltamiento de secciones de código Python.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
>>> print CODE('print "hello"', language='python').xml()
<table><tr valign="top"><td style="width:40px; text-align: right;"><pre style="
        font-size: 11px;
        font-family: Bitstream Vera Sans Mono,monospace;
        background-color: transparent;
            margin: 0;
            padding: 5px;
            border: none;
        background-color: #E0E0E0;
        color: #A0A0A0;
    ">1.</pre></td><td><pre style="
        font-size: 11px;
        font-family: Bitstream Vera Sans Mono,monospace;
        background-color: transparent;
            margin: 0;
            padding: 5px;
            border: none;
            overflow: auto;
    "><span style="color:#185369; font-weight: bold">print </span>
    <span style="color: #FF9966">"hello"</span></pre></td></tr>
</table>

Un ejemplo similar para HTML

1
2
3
>>> print CODE(
>>>   '<html><body>{{=request.env.remote_add}}</body></html>',
>>>   language='html')
1
2
3
<table>...<code>...
<html><body>{{=request.env.remote_add}}</body></html>
...</code>...</table>

Estos son los argumentos por defecto del ayudante CODE:

1
CODE("print 'hello world'", language='python', link=None, counter=1, styles={})

Los valores soportados por el argumento language son “python”, “html_plain”, “c”, “cpp”, “web2py”, y “html”. El lenguaje “html” interpreta etiquetas {{ and }} como código “web2py”, mientras “html_plain” no lo hace.

Si un valor link se especifica, por ejemplo “/examples/global/vars/”, las referencias al API de web2py en el código se enlazan a documentación en el enlace URL. Por ejemplo, “request” podría ser enlezado a “/examples/global/vars/request”. En el ejemplo de arriba, el enlace URL es manejado por la acción “var” en el controlador” global.py” que es distribuído como parte de los “ejemplos” de aplicaciones.

El argumento counter se usa para numeración de líneas. A este se le puede asignar tres valores distintos. Puede ser None para no numerar las líneas, un valor numérico especificando el número de comienzo, o una cadena. Si el contador es una cadena, se interpreta como un prompt, y no se numeran las líneas.

DIV

Todos los ayudantes, aparte del XML se derivan de DIV y heredan su métodos básicos.

1
2
>>> print DIV('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<div id="0" class="test">&lt;hello&gt;<b>world</b></div>

EM

Enfatiza su contenido.

1
2
>>> print EM('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<em id="0" class="test">&lt;hello&gt;<b>world</b></em>

FIELDSET

Esto se usa para crear un campo de entrada junto con su etiqueta.

1
2
>>> print FIELDSET('Height:', INPUT(_name='height'), _class='test')
<fieldset class="test">Height:<input name="height" /></fieldset>

FORM

Este es uno de los ayudantes mas importantes. En su forma simple, solo hace un etiqueta <form>...</form>, pero porque los ayudantes son objetos y saben lo que contienen, pueden procesar formularios que se les hayan enviado (por ejemplo, hacer validación de campos). Esto se discutirá en detalle en el Capítulo 7.

1
2
3
>>> print FORM(INPUT(_type='submit'), _action=", _method='post')
<form enctype="multipart/form-data" action="" method="post">
<input type="submit" /></form>

El “enctype” es “multipart/form-data” por defecto.

El constructor de una FORM, y de una SQLFORM*, puede también tomar un argumento especial llamado hidden. Cuando un diccionario se pasa como hidden, sus elementos se traducen como campos “hidden” de ENTRADA. Por ejemplo:

1
2
3
>>> print FORM(hidden=dict(a='b'))
<form enctype="multipart/form-data" action="" method="post">
<input value="b" type="hidden" name="a" /></form>

H1, H2, H3, H4, H5, H6

Estros ayudantes son para encabezados de párrafos y subpárrafos:

1
2
>>> print H1('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<h1 id="0" class="test">&lt;hello&gt;<b>world</b></h1>

HEAD

Para etiquetar el encabezado (HEAD) de una página HTML.

1
2
>>> print HEAD(TITLE('<hello>', XML('<b>world</b>')))
<head><title>&lt;hello&gt;<b>world</b></title></head>

HTML

Este ayudante es un poco diferente. Además de hacer las etiquetas <html>, le antepone a la etiqueta una cadena doctype.

1
2
3
4
>>> print HTML(BODY('<hello>', XML('<b>world</b>')))
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
                      "http://www.w3.org/TR/html4/loose.dtd">
<html><body>&lt;hello&gt;<b>world</b></body></html>

El ayudante HTML también toma algnos argumentos opcionales que por defecto son:

1
HTML(..., lang='en', doctype='transitional')

Donde doctype puede ser ‘strict’, ‘transitional’, ‘frameset’, ‘html5’, o una cadena doctype completa.

XHTML

XHTML es similar a HTML pero en cambio crea un XHTML doctype.

1
XHTML(..., lang='en', doctype='transitional', xmlns='http://www.w3.org/1999/xhtml')

Donde doctype puede ser ‘strict’, ‘transitional’, ‘frameset’, o una cadena doctype completa.

INPUT

Crea una etiqueta <input.../>. Una etiqueta de entrada no puede contener otras etiquetas, y se cierra con /> en vez de >. La etiqueta de entrada tiene un atributo _type opcional que puede ser asignado a “text” (por defecto), “submit”, “checkbox”, o “radio”.

1
2
>>> print INPUT(_name='test', _value='a')
<input value="a" name="test" />

También toma unos argumentos opcionales especiales llamados “value”, distinct from “_value”. Este último pone el valor por defecto del campo entrada; el primero le dá el valor actual. Para una entrada del tipo “text”, el primero anula el último:

1
2
>>> print INPUT(_name='test', _value='a', value='b')
<input value="b" name="test" />

Para botones circulares INPUT selectivamente pone el atributo “chequeado”:: radio

1
2
3
4
5
>>> for v in ['a', 'b', 'c']:
>>>     print INPUT(_type='radio', _name='test', _value=v, value='b'), v
<input value="a" type="radio" name="test" /> a
<input value="b" type="radio" checked="checked" name="test" /> b
<input value="c" type="radio" name="test" /> c

Y lo hace de manera similar con cuadros de validación:

1
2
3
4
>>> print INPUT(_type='checkbox', _name='test', _value='a', value=True)
<input value="a" type="checkbox" checked="checked" name="test" />
>>> print INPUT(_type='checkbox', _name='test', _value='a', value=False)
<input value="a" type="checkbox" name="test" />

IFRAME

Este ayudante incluye otra página web en la actual. El URL de la otra página se especifica a través del atributo “_src”.

1
2
>>> print IFRAME(_src='http://www.web2py.com')
<iframe src="http://www.web2py.com"></iframe>

IMG

Puede ser usado para incrustar imágenes en HTML:

1
2
>>> IMG(_src='http://example.com/image.png',_alt='test')
<img src="http://example.com/image.ong" alt="rest" />

Aquí hay una combinación de ayudantes A, IMG, y URL para incluir imágenes estáticas con un enlace:

1
2
3
4
5
>>> A(IMG(_src=URL('static','logo.png'), _alt="My Logo")
      _href=URL('default','index'))
<a href="/myapp/default/index">
  <img src="/myapp/static/logo.png" alt="My Logo" />
</a>

LABEL

Se usa para crear una etiqueta LABEL para un campo INPUT.

1
2
>>> print LABEL('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<label id="0" class="test">&lt;hello&gt;<b>world</b></label>

LI

Hace una lista de artículos que debe estar contenida en una etiqueta UL o OL.

1
2
>>> print LI('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<li id="0" class="test">&lt;hello&gt;<b>world</b></li>

LEGEND

Se usa para crear una etiqueta de leyenda por un campo en un formulario.

1
2
>>> print LEGEND('Name', _for='myfield')
<legend for="myfield">Name</legend>

META

Se usa para construir etiquetas META en el encabezado HTML. Por ejemplo:

1
2
>>> print META(_name='security', _content='high')
<meta name="security" content="high" />

MARKMIN

Implementa la sintáxis markwin wiki. Convierte el texto de entrada en salida html de acuerdo con las reglas markwin descritas en el ejemplo de abajo:

1
2
3
4
5
>>> print MARKMIN("'this is **bold** or ''italic'' and this [[a link http://web2py.com]]"')
<p>this is <b>bold</b> or <i>italic</i> and
this <a href="http://web2py.com">a link</a></p>
La sintáxis markwin se describe en este archivo que se empaqueta con web2py:
http://127.0.0.1:8000/examples/static/markmin.html

y algunos ejemplos se muestran en el capítulo 13 en el contexto de plugin_wiki el cual usa MARKMIN de forma extensiva.

OBJECT

Usado para incrustar objetos (por ejemplo, unflash player) en el HTML.

1
2
3
>>> print OBJECT('<hello>', XML('<b>world</b>'),
>>>              _src='http://www.web2py.com')
<object src="http://www.web2py.com">&lt;hello&gt;<b>world</b></object>

OL

Indica Lista Ordenada. La lista debe contener etiquetas LI. Argumentos OL que no son objetos LI se encierran automáticamente en etiquetas <li>...</li>.

1
2
>>> print OL('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<ol id="0" class="test"><li>&lt;hello&gt;</li><li><b>world</b></li></ol>

ON

Esto está aquí para la compatibilidad con versiones anteriores y es simplemente un alias para True. Se usa excusivamente para cajas de validación y es obsoleto dado que True es mas Pythonico.

1
2
>>> print INPUT(_type='checkbox', _name='test', _checked=ON)
<input checked="checked" type="checkbox" name="test" />

OPTGROUP

Permite agrupar multiples opciones en un SELECT y es útil para personalizar campos usando CSS.

1
2
3
4
5
6
7
8
>>> print SELECT('a', OPTGROUP('b', 'c'))
<select>
  <option value="a">a</option>
  <optgroup>
    <option value="b">b</option>
    <option value="c">c</option>
  </optgroup>
</select>

OPTION

Solo debe usarse como parte de la combinación SELECT/OPTION.

1
2
>>> print OPTION('<hello>', XML('<b>world</b>'), _value='a')
<option value="a">&lt;hello&gt;<b>world</b></option>

Como en el caso de INPUT, web2py hace la distinción entre “_value” (el valor de OPTION), y “value” (el valor actual del select que lo encierra). Si son iguales, la opción se “selecciona”.

1
2
3
4
5
>>> print SELECT('a', 'b', value='b'):
<select>
<option value="a">a</option>
<option value="b" selected="selected">b</option>
</select>

P

Esto es para ponerle etiquetas a un párrafo.

1
2
>>> print P('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<p id="0" class="test">&lt;hello&gt;<b>world</b></p>

PRE

Genera una etiqueta <pre>...</pre> para mostrar texto preformateado. El ayudante CODE es por lo general preferido para listas de código.

1
2
>>> print PRE('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<pre id="0" class="test">&lt;hello&gt;<b>world</b></pre>

SCRIPT

Este incluye o enlaza un script, tal como JavaScript. El contenido entre las etiquetas se presenta como un comentario HTML, en beneficio de navegadores verdaderamente viejos.

1
2
3
4
>>> print SCRIPT('alert("hello world");', _language='javascript')
<script language="javascript"><!--
alert("hello world");
//--></script>

SELECT

Hace una etiqueta <select>...</select>. Este se usa con el ayudante OPTION. Estos argumentos SELECT que no son objetos de OPTION se convierten automáticamente a options.

1
2
3
4
5
>>> print SELECT('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<select id="0" class="test">
   <option value="&lt;hello&gt;">&lt;hello&gt;</option>
   <option value="&lt;b&gt;world&lt;/b&gt;"><b>world</b></option>
</select>

SPAN

Similar a DIV pero se usa para etiquetar (en vez de bloquear) contenido.

1
2
>>> print SPAN('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<span id="0" class="test">&lt;hello&gt;<b>world</b></span>

STYLE

Similar a script, pero usado para incluir o enlazar código CSS. Aquí el código CSS se incluye:

1
2
3
4
>>> print STYLE(XML('body {color: white}'))
<style><!--
body { color: white }
//--></style>

Y aquí se enlaza:

1
2
3
>>> print STYLE(_src='style.css')
<style src="style.css"><!--
//--></style>

TABLE, TR, TD

Estas etiquetas (lo mismo que los ayudantes opcionales THEAD, TBODY y TFOOTER ) se usan para construir tablas HTML.

1
2
>>> print TABLE(TR(TD('a'), TD('b')), TR(TD('c'), TD('d')))
<table><tr><td>a</td><td>b</td></tr><tr><td>c</td><td>d</td></tr></table>

TR espera contenido TD; los argumentos que no son objetos TD se convierten automáticamente.

1
2
>>> print TABLE(TR('a', 'b'), TR('c', 'd'))
<table><tr><td>a</td><td>b</td></tr><tr><td>c</td><td>d</td></tr></table>

Es fácil convertir un arreglo Python en una tabla HTML usando la notación de función de argumentos Python *, la cual mapea elementos de una lista como argumentos de funcion posicional.

Aquí se hace línea a línea:

1
2
3
>>> table = [['a', 'b'], ['c', 'd']]
>>> print TABLE(TR(*table[0]), TR(*table[1]))
<table><tr><td>a</td><td>b</td></tr><tr><td>c</td><td>d</td></tr></table>

Aquí se hacen todas las líneas a la vez:

1
2
3
>>> table = [['a', 'b'], ['c', 'd']]
>>> print TABLE(*[TR(*rows) for rows in table])
<table><tr><td>a</td><td>b</td></tr><tr><td>c</td><td>d</td></tr></table>

TBODY

Esto se usa para etiquetar filas contenidas en el cuerpo de la tabla, al contrario de hacerlo para filas de encabezados o de pies de página. Es opcional.

1
2
>>> print TBODY(TR('<hello>'), _class='test', _id=0)
<tbody id="0" class="test"><tr><td>&lt;hello&gt;</td></tr></tbody>

TEXTAREA

Este ayudante hace una etiqueta <textarea>...</textarea>.

1
2
>>> print TEXTAREA('<hello>', XML('<b>world</b>'), _class='test')
<textarea class="test" cols="40" rows="10">&lt;hello&gt;<b>world</b></textarea>

El único problema es que su “valor” opcional anula su contenido (HTML interno)

1
2
>>> print TEXTAREA(value="<hello world>", _class="test")
<textarea class="test" cols="40" rows="10">&lt;hello world&gt;</textarea>

TFOOT

Esto es usado para etiquetar filas píes de tabla.

1
2
>>> print TFOOT(TR(TD('<hello>')), _class='test', _id=0)
<tfoot id="0" class="test"><tr><td>&lt;hello&gt;</td></tr></tfoot>

TH

Esto se usa en vez de TD en encabezados de tablas.

1
2
>>> print TH('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<th id="0" class="test">&lt;hello&gt;<b>world</b></th>

THEAD

Esto se usa para etiquetar filas de encabezados de tablas.

1
2
>>> print THEAD(TR(TD('<hello>')), _class='test', _id=0)
<thead id="0" class="test"><tr><td>&lt;hello&gt;</td></tr></thead>

TITLE

Esto es usado para etiquetar el título de una página en un encabezado HTML.

1
2
>>> print TITLE('<hello>', XML('<b>world</b>'))
<title>&lt;hello&gt;<b>world</b></title>

TR

Etiqueta una fila de una tabla. Debe ser mostrado dentro de una tabla y contener las etiquetas <td>...</td>. Los argumentos de TR que no son objetos TD se convertirán automáticamente.

1
2
>>> print TR('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<tr id="0" class="test"><td>&lt;hello&gt;</td><td><b>world</b></td></tr>

TT

Etiqueta texto como texto de máquina de escribir (monoespaciado).

1
2
>>> print TT('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<tt id="0" class="test">&lt;hello&gt;<b>world</b></tt>

UL

Tiene el significado de una lista no ordenada y debe contener elementos LI. Si su contenido no se etiqueta como LI, UL lo hace automáticamente.

1
2
>>> print UL('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<ul id="0" class="test"><li>&lt;hello&gt;</li><li><b>world</b></li></ul>

Ayudantes personalizados

Algunas veces se necesita generar etiquetas personalizadas XML. web2py provee TAG, un generador universal de etiquetas.

{{=TAG.name('a', 'b', _c='d')}}

Genera el siguiente XML

1
<name c="d">ab</name>

Los argumentos “a” y “b” y “d” se escapan automáticamente; se usa el ayudante XML para eliminar este comportamiento. Usando TAG se pueden generar etiquetas HTML/XML que no han sido ya provistas por la API. Las TAGs pueden anidarse, y se serializan con str(). Una sintáxis equivalente es:

{{=TAG['name']('a', 'b', c='d')}}

Note que TAG es un objeto, y TAG.name o TAG[‘name’] es una función que devuelve una clase temporal de ayudante.

MENU

El ayudante MENU toma una lista de listas de la forma de response.menu (tal como se describe en el capítulo 4) y genera una estructura de árbol usando listas no ordenadas que representan el menú. Por ejemplo:

1
2
3
4
5
>>> print MENU([['One', False, 'link1'], ['Two', False, 'link2']])
<ul class="web2py-menu web2py-menu-vertical">
  <li><a href="link1">One</a></li>
  <li><a href="link2">Two</a></li>
</ul>

Cada elemento del menú puede tener un cuarto argumento que es un submenú anidado (y asi de manera recursiva):

1
2
3
4
5
6
7
8
9
>>> print MENU([['One', False, 'link1', [['Two', False, 'link2']]]])
<ul class="web2py-menu web2py-menu-vertical">
  <li class="web2py-menu-expand">
     <a href="link1">One</a>
     <ul class="web2py-menu-vertical">
        <li><a href="link2">Two</a></li>
     </ul>
  </li>
</ul>

El ayudante MENU toma opcionalmente los siguientes argumentos:

  • _class: por defecto “web2py-menu web2py-menu-vertical” y fija la clase de los elementos UL externos.
  • ul_class: por defecto “web2py-menu-vertical” y fija la clase de los elementos UL internos.
  • li_class: por defecto “web2py-menu-expand” y fija la clase de los elementos LI internos.

La “base.css” de la aplicación andamio entiende los siguientes tipos básicos de menús: “web2py-menu web2py-menu-vertical” y “web2py-menu web2py-menu-horizontal”.

BEAUTIFY

BEAUTIFY se usa para construir representaciones HTML de objetos compuestos, incluyendo listas, tuplas y diccionarios:

{{=BEAUTIFY({"a":["hello", XML("world")], "b":(1, 2)})}}

BEAUTIFY devuelve un objeto parecido a XML, serializable a XML, con una agradable presentación de su argumento constructor. En este caso, la representación XML de:

1
{"a":["hello", XML("world")], "b":(1, 2)}

Se presentará como:

1
2
3
4
<table>
<tr><td>a</td><td>:</td><td>hello<br />world</td></tr>
<tr><td>b</td><td>:</td><td>1<br />2</td></tr>
</table>

DOM del lado del servidor y Parsing

El ayudante DIV y todos los ayudantes derivados proveen dos métodos de búsqueda: : element y elements. element que retorna el primer elemento “hijo” que cumple una condición especificada (o None (ninguna) si no se cumple). elements devuelve una lista de todos los “hijos”que cumplen la condición.

element y elements usan la misma sintáxis para especificar la condición que debe coincidir lo cual permite tres posibilidades que pueden mezclarse y emparejarse : expresiones como jQuery, coincidencia con el valor del atributo exacto, coincidencia usando expresiones regulares.

Aquí hay un ejemplo simple:

1
2
3
4
5
>>> a = DIV(DIV(DIV('a', _id='target',_class='abc')))
>>> d = a.elements('div#target')
>>> d[0] = 'changed'
>>> print a
<div><div><div id="target" class="abc">changed</div></div></div>

El argumento sin nombre de elementos es una cadena que puede contener: el nombre de una etiqueta, la identificación de una etiqueta precedida por el símbolo #, la clase precedida por un punto, el valor explícito del atributo en paréntesis cuadrados.

Aquí hay cuatros formas equivalentes de buscar la etiqueta anterior por su identificador

1
2
3
4
>>> d = a.elements('#target')
>>> d = a.elements('div#target')
>>> d = a.elements('div[id=target]')
>>> d = a.elements('div',_id='target')

Aquí hay cuatros formas equivalentes de buscar la etiqueta anterior por su clase

1
2
3
4
>>> d = a.elements('.abc')
>>> d = a.elements('div.abc')
>>> d = a.elements('div[class=abc]')
>>> d = a.elements('div',_class='abc')

Cualquier atributo puede ser usado para localizar un elemento (no solo id and class), incluyendo atributos múltiples (la función element puede tomar múltiples argumentos con nombres) pero solo el primer elemento que coincida será devuelto.

Usando la sintáxis jQuery “div#target” es posible especificar múltiples criterios de búsqueda separados por un espacio:

1
2
>>> a = DIV(SPAN('a', _id='t1'),div('b',_class='c2'))
>>> d = a.elements('span#t1, div#c2')

o de forma equivalente

1
2
>>> a = DIV(SPAN('a', _id='t1'),div('b',_class='c2'))
>>> d = a.elements('span#t1','div#c2')

Si el valor del atributo de búsqueda se especifica usando un argumento con nombre, este puede ser una cadena o una expresión regular:

1
2
>>> a = DIV(SPAN('a', _id='test123'),div('b',_class='c2'))
>>> d = a.elements('span',_id=re.compile('test\d{3}')

Un argumento especial con nombre del ayudante DIV (y sus derivados) es find. Puede usarse para especificar un valor de búsqueda o una expresión de búsqueda regular en el contenido del texto de la etiqueta. Por ejemplo:

1
2
3
4
>>> a = DIV(SPAN('abcde'),div('fghij'))
>>> d = a.elements(find='bcd')
>>> print d[0]
<span>abcde</span>

o

1
2
3
4
>>> a = DIV(SPAN('abcde'),div('fghij'))
>>> d = a.elements(find=re.compile('fg\w{3}'))
>>> print d[0]
<div>fghij</div>

components

He aquí un ejemplo del listado de todos los elementos de una cadena html:

1
2
html = TAG('<a>xxx</a><b>yyy</b>')
for item in html.components: print item

parent

parent devuelve el “padre” del elemento actual.

1
2
3
4
5
>>> a = DIV(SPAN('a'),DIV('b'))
>>> d = a.element('a').parent()
>>> d['_class']='abc'
>>> print a
<div class="abc"><span>a</span><div>b</div></div>

flatten

El método recursivo flatten serializa el contenido de los “hijos”de un elemento dado en un texto regular (sin etiquetas):

1
2
3
>>> a = DIV(SPAN('this',DIV('is',B('a'))),SPAN('test'))
>>> print a.flatten()
thisisatest

Flatten puede pasarse como un argumento opcional, render, por ejemplo. Una función que presenta/simplifica el contenido usando un protocolo diferente. He aquí un ejemplo de como serializar algunas etiquetas en sintáxis Markmin wiki:

1
2
3
4
5
>>> a = DIV(H1('title'),P('example of a ',A('link',_href='#test')))
>>> from gluon.html import markmin_serializer
>>> print a.flatten(render=markmin_serializer)
# titles
example of [[a link #test]]

Al momento de escribir esto se provee markmin_serializer y markdown_serializer.

Parsing

El objeto TAG es también un analizador XML/HTML. Puede leer texto y convertirlo en una estructura de árbol de ayudantes. Esto permite manipulación usando el API mostrado arriba:

1
2
3
4
5
>>> html = '<h1>Title</h1><p>this is a <span>test</span></p>'
>>> parsed_html = TAG(html)
>>> parsed_html.element('span')[0]='TEST'
>>> print parsed_html
<h1>Title</h1><p>this is a <span>TEST</span></p>

Disposición de página

Las vistas pueden extenderse e incluir otras vistas en una estructura de árbol.

Por ejemplo, puede pensarse que la vista “index.html” extiende “layout.html” e incluye “body.html”. Al mismo tiempo “layout.html” puede incluir “header.html” y “footer.html”.

La raíz del árbol es lo que se llama una vista de la disposición. Tal como otro archivo de plantillas HTML, puede editarse usando la interfaz administrativa de web2py. El nombre del archivo “layout.html” es solo una convención.

Aquí está la página minimalista que extiende la vista “layout.html” view e incluye la vista “page.html”:

1
2
3
{{extend 'layout.html'}}
<h1>Hello World</h1>
{{include 'page.html'}}

El archivo de diseño extendido debe contener una directiva {{include}}, algo como:

1
2
3
4
5
<html><head><title>Page Title</title></head>
  <body>
    {{include}}
  </body>
</head>

Cuando se llama la vista, la vista (diseño) extendida se carga, y la vista llamada reemplaza la directiva {{include}} en la disposición. El procesamiento continúa recursivamente hasta que todas las directrices de extensión e inclusión se han procesado. La plantilla que resulta se traduce entonces a código Python.

extend e include son directivas especiales de plantillas, no comandos Python.

Las disposiciones se usan para encapsular cosas comunes de la página (encabezados, pies de página, menús), y aunque no son obligatorias, harán la aplicación fácil de escribir y mantener. En particular, se sugiere escribir disposiciones que tomen ventaja de las siguientes variables que pueden ser asignadas en el controlador. Usar estas bien conocidas variables ayudará a hacer las disposiciones intercambiables:

1
2
3
4
5
6
7
8
response.title
response.subtitle
response.meta.author
response.meta.keywords
response.meta.description
response.flash
response.menu
response.files

Excepto por menu y files estás son todas cadenas y su significado debería ser obvio.

El menú response.menu es una lista de tres o cuatro tuplas. Los tres elementos son: el nombre del enlace, un lógico representando si el enlace está activo (es el enlace actual), y la URL de la página enlazada. Por ejemplo:

1
2
response.menu = [('Google', False, 'http://www.google.com',[]),
                 ('Index',  True,  URL('index'), [])]

El cuarto elemento de la tupla es un submenú opcional.

response.files es una lista de archivos CSS y JS que se necesitan en la página.

También se recomienda usar:

1
{{include 'web2py_ajax.html'}}

En el encabezado HTML, dado que este incluirán las librerías jQuery y definirán algunas funciones JavaScript compatibles con versiones anteriores para efectos especiales y Ajax. “web2py_ajax.html” incluye la etiqueta response.meta en la vista, jQuery base, el calendario jQuery e incluye todos los CSS y JS response.files.

Disposición de página por defecto

aquí se vé el mínimo “views/layout.html” que viene en web2py con la aplicación andamio welcome, Y cada nueva aplicación será similar a la aplicación por defecto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="{{=T.accepted_language or 'en'}}">
  <head>
    <title>{{=response.title or request.application}}</title>
    <link rel="shortcut icon"
      href="{{=URL(request.application,'static','favicon.ico')}}"
      type="image/vnd.microsoft.icon">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    {{######  require CSS and JS files for this page (read info in base.css) }}
    {{response.files.append(URL(request.application,'static','base.css'))}}
    {{response.files.append(URL(request.application,'static','superfish.js'))}}
    {{###### include web2py specific js code (jquery, calendar, form stuff) }}
    {{include 'web2py_ajax.html'}}
  </head>
  <body>
    <div class="flash">{{=response.flash or ''}}</div>
    <div class="ez-mr wrapper" id="layout">
      {{###### Layout 3 from http://www.ez-css.org/layouts }}
      <div class="ez-wr">
 <div class="ez-box" id="header">
   {{try:}}{{=auth.navbar(action=URL(request.application,'default','user'))}}{{except:pass}}
          <h1>
            <a href="">{{=response.title or 'response.title'}}</a>
          </h1>
          <h2>
        {{=response.subtitle or 'response.subtitle'}}
      </h2>
    </div>
    <div class="ez-box" id="statusbar">
      {{###### superfish menu }}
   {{=MENU(response.menu,_class='sf-menu')}}
          <script>
        jQuery(document).ready(function(){
              jQuery('ul.sf-menu').superfish({delay:400});});
      </script>
    </div>
    <div class="ez-wr">
      <div class="ez-fl ez-negmx">
            <div class="ez-box" id="left_sidebar">{{###### unused space}}</div>
   </div>
   <div class="ez-fl ez-negmr">
            <div class="ez-box" id="content">{{include}}</div>
      </div>
      <div class="ez-last ez-oh">
            <div class="ez-box" id="right_sidebar">{{###### unused space}}</div>
   </div>
 </div>
 <div class="ez-box" id="footer">
          {{=T('Copyright')}} © 2010 -
          {{=T('Powered by')}} <a href="http://www.web2py.com">web2py</a>
    </div>
      </div>
    </div>
  </body>
</html>

Hay algunas características de esta disposición por defecto que la hace muy fácil de usar y personalizar:

  • {{#...}} son comentarios especiales que no apareceran en el HTML.
  • Despliega tanto response.title como response.subtitle los cuales pueden ser asignados en un modelo. Si no son asignados, se adopta el nombre de la aplicación como título.
  • Incluye el archivo web2py_ajax.html en el encabezado.
  • Requiere explícitamente dos archivos: “base.css” y “superfish.js”. El último contiene el CSS completo para la pagina y está muy bien documentado y listo para personalizar. El primero contiene el JS para el menú de cascada por defecto.
  • La {{=auth.navbar(...)}} despliega una bienvenida al usuario actual y enlaza funciones auth como login, logout, register, change password, etc. dependiendo del contexto. Esto se coloca en un {{try:}}...{{except:pass}} en caso de que auth no esté definido.
  • El {{=MENU(response.menu) despliega la estructura del menú como <ul>...</ul>.
  • Hay un script explícito para activar el menú de cascada superfish cascading menu y este puede removerse si no es necesario.
  • {{include}} se reemplaza por el contenido de la vista extendida cuando se presenta la página.
  • Este usa por defecto una disposición de página de tres columnas y usa los siguientes identificadores DIV: “header”, “left_sidebar”, “content”, y “right_sidebar”, “footer” aunque el que ya se provee, “base.css”, asigna los anchos de las barras laterales a cero.
  • Este usa la convención ez.css para la convención CSS nombrada definida en la referencia 51. En particular usa el número Layout 3. La ez.css minimizada se incluye en “base.css”.

Personalizando la disposición por defecto

Personalizar la disposición por defecto sin editar es muy fácil porque el archivo “static/base.css” está muy bien documentado.

En particular está organizado en las siguientes subsecciones:

  • ez.css .
  • reset common tags - etiquetas comunes para reiniciar
  • choose default fonts – escoger tipos de letra por defecto
  • choose link style – escoger el estilo de los enlaces
  • add bottom line to table rows – añadir línea final a filas de tablas
  • labels bold and occasionally centered – etiquetas resaltadas y ocasionalmente centradas
  • make all input fields the same size – hacer los campos de entrada del mismo tamaño
  • add proper separation between h1-h6 and text – Agregar la separación apropiada entre h1-h6 y texto
  • always indent the first line and add space below paragraphs – Dar sangría siempre a la primera línea y agregar espacio debajo de los párrafos
  • bullets and numbers style and indent – viñetas, estilos de números y sangría
  • form and table padding – relleno de formas y tablas
  • code blocks – código de bloques
  • left and right padding to quoted text – relleno de textos a izquierda y derecha.
  • page layout alignment, width and padding (change this for spaces) – alineación de la disposición de la página, ancho y relleno (cambiar esto por espacios)
  • column widths (change this to use left_sidebar and right_sidebar) – Anchos de columnas (cambiar esto por el uso de la barra lateral izquierda y la barra lateral derecha)
  • background images and colors (change this for colors) – Imágenes de fondo y colores (cambiar esto por colores)
  • menu style (for superfish.js) – estilo del menú (para superfish.js)
  • web2py specific (.flash, .error) – Especificar web2py (.flash, .error)

Para cambiar left_sidebar, content, right_sidebar widths, simplemente se edita parte de “base.css”:

1
2
3
4
/*********** column widths ***********/
#left_sidebar { width: 0px; }
#content { width: 840px; }
#right_sidebar { width: 0px; }

Para cambiar colores e imágenes en el fondo, simplemente se edita lo siguiente:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/*********** backrgound images and colors ***********/
body { background: url('/book/static/book_images_png/background.png') repeat-x #3A3A3A; }
a { color: #349C01; }
.auth_navbar {
   top: 0px;
   float: right;
   padding: 3px 10px 3px 10px;
   font-size: 0.9em;
}
code { color: green; background: black; }
input:focus, textarea:focus { background: #ccffcc; }
#layout { background: white; }
#header, #footer { color: white; background: url('/book/static/book_images_png/header.png') repeat #111111;}
#header h1 { color: #349C01; }
#header h2 { color: white; font-style: italic; font-size: 14px;}
#statusbar { background: #333333; border-bottom: 5px #349C01 solid; }
#statusbar a { color: white; }
#footer { border-top: 5px #349C01 solid; }

El menú se construye de manera que se usan colores neutros pero eso también puede cambiarse.

Claro que puede también cambiarse completamente los archivos “layout.html” y “base.css” con archivos propios.

Funciones en vistas

Se considera este “layout.html”:

1
2
3
4
5
6
7
8
9
<html>
  <body>
    {{include}} <!-- must come before the two blocks below -->
    <div class="sidebar">
      {{if 'mysidebar' in globals():}}{{mysidebar()}}{{else:}}
        my default sidebar
      {{pass}}
  </body>
</html>

Y su vista extendida

1
2
3
4
5
{{def mysidebar():}}
my new sidebar!!!
{{return}}
{{extend 'layout.html'}}
Hello World!!!

Note la función definida antes de la declaración {{extend...}}. También note que la función se incluye en la vista extendida sin el prefijo =.

El código genera la siguiente salida:

1
2
3
4
5
6
7
8
9
<html>
  <body>
    Hello World!!!
    <div class="sidebar">
      {{block mysidebar}}
        my new sidebar!!!
      {{end}}
  </body>
</html>

Note que las funciones están definidas en HTML (aunque estas también pueden contener código Python ) de tal forma que response.write se usa para escribir su contenido (las funciones no retornan el contenido). Esta es la razón por la cual la disposición llama a la función de la vista usando {{mysidebar()}} en vez de {{=mysidebar()}}. Las funciones definidas de esta forma pueden tomar argumentos.

Bloques en vistas

Otra forma de hacer una vista mas modular es usando {{block...}}s y este mecanismo es una alternativa para el mecanismo utilizado en la sección anterior.

Considere este “layout.html”:

1
2
3
4
5
6
7
8
9
<html>
  <body>
    {{include}} <!-- must come before the two blocks below -->
    <div class="sidebar">
      {{block mysidebar}}
        my default sidebar
      {{end}}
  </body>
</html>

Y su vista extendida

1
2
3
4
5
{{extend 'layout.html'}}
Hello World!!!
{{block mysidebar}}
my new sidebar!!!
{{end}}

Este genera la siguiente salida:

1
2
3
4
5
6
7
8
9
<html>
  <body>
    Hello World!!!
    <div class="sidebar">
      {{block mysidebar}}
        my new sidebar!!!
      {{end}}
  </body>
</html>

Se pueden tener muchos bloques y si el bloque está presente en la vista extendida pero no en la vista ampliada, se usa el contenido de la vista extendida.

Usando el sistema de plantillas para generar Emails

Es posible usar el sistema de plantillas para generar emails. Por ejemplo considere la tabla de base de datos

1
db.define_table('person', Field('name'))

Donde se quiere enviar a cada persona en la base de datos el siguiente mensaje, guardado en un archivo vista llamado “message.html”:

1
2
Dear {{=person.name}},
You have won the second prize, a set of steak knives.

Se puede lograr de la siguiente manera:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
>>> from gluon.tool import Mail
>>> mail = Mail(globals())
>>> mail.settings.server = 'smtp.gmail.com:587'
>>> mail.settings.sender = '...@somewhere.com'
>>> mail.settings.login = None or 'username:password'
>>> for person in db(db.person.id>0).select():
>>>     context = dict(person=person)
>>>     message = response.render('message.html', context)
>>>     mail.send(to=['who@example.com'],
>>>               subject='None',
>>>               message=message)

La mayor parte del trabajo se hace en la sentencia

1
response.render('message.html', context)

Esta presenta la vista “file.html” con las variables definidas en el diccionario “context”, y este retorna una cadena con el texto del email presentado. El contexto es un diccionario que contiene variables que serán visibles para el archivo plantilla.

Si el mensaje comienza con cd <html> y termina con </html> el email será un email HTML.

El mismo mecanismo que se usa para generar texto de email puede ser usado para generar SMS u otro tipo de mensaje basado en la plantilla.