URL amigables en web2py

web2py utiliza una notación para las direcciones de URL en concordancia con el patrón de desarrollo MVC. De este modo, el esquema por defecto para una aplicación web en este framework es el siguiente.

http://.../aplicacion/controlador/funcion

Por ejemplo, en la dirección

http://www.web2py.com/init/default/what

init indica el nombre de una aplicación, default es un controlador y what una función definida dentro del archivo default.py.

Si bien para aplicaciones con enfoque personal o corporativo esto no resulta una inquietud, aquellas orientadas al usuario convencional (una red social, un portal de noticias, un blog) requieren de direcciones más agradables para el consumidor. Además, incorporar URL amigables optimiza el posicionamiento en buscadores (SEO).

Por ejemplo, sería más conveniente acceder a un formulario de registro vía:

miweb.com/crear-una-cuenta

en oposición a:

miweb.com/myapp/default/user/login

Por otro lado, esta guía también te permitirá soportar URL antiguas si estás usando web2py para reemplazar una aplicación web obsoleta pero quieres mantener compatibilidad.

Para conseguir esto, estaremos empleando la capacidad de web2py de reescribir direcciones de URL. Se trata de escribir un conjunto de reglas en el archivo routes.py (ubicado en el directorio de instalación de web2py, y si no existe puedes crearlo) para indicar cómo deben comportarse las URL. Nótese que esto no quebrará las antiguas direcciones de tu aplicación, y no tendrás que reemplazar ninguna dirección en tus vistas o controladores, siempre y cuando hayas usado la función URL.

web2py provee dos métodos para configurar direcciones de URL. Un sistema básico basado en parámetros y otro (no tanto) más complejo fundado en patrones. Ambos métodos no pueden mezclarse, y para emplear URL amigables es necesario utilizar aquél basado en patrones. Si ya cuentas con una aplicación que hace uso del sistema basado en parámetros, no hay de qué preocuparse, migrar de un método a otro no es una tarea ardua.

Empezamos por abrir el archivo routes.py o bien crearlo si no existe. Si dentro del archivo encuentras lo siguiente:

routers = dict(
    # base router
    BASE=dict(
        default_application='welcome',
    ),
)

Eso indica que estás usando el sistema basado en parámetros. Allí está definido que la aplicación por defecto es «welcome», de modo que http://.../ equivale a http://.../welcome/default/index. Para cambiar la aplicación por defecto en el sistema basado en patrones, vamos a reemplazar el código anterior (completo) por simplemente:

default_application = "myapp"

Para observar los cambios es necesario recargar las rutas, reiniciando web2py.

Si tu archivo de rutas definía routes_onerror puedes conservarlo intacto ya que es soportado por ambos sistemas.

Ahora bien, el sistema basado en patrones consiste en definir URL de entrada (routes_in) y de salida (routes_out). Ambas listas contendrán tuplas indicando, en primer lugar, un patrón y, en segundo lugar, la dirección de URL con la que está asociado.

Por ejemplo, si contamos con un formulario de registro en /myapp/default/register, podemos enlazarlo con /crear-una-cuenta de la siguiente forma:

routes_in = [
    ("/crear-una-cuenta", "/myapp/default/register")
]

En este caso, el formulario puede ser accedido a través de ambas direcciones. Además, queremos que la función URL retorne /crear-una-cuenta al llamar a URL("default", "register"). Para esto hacemos uso de routes_out a continuación del código anterior.

routes_out = [
    ("/myapp/default/register", "/crear-una-cuenta")
]

Como puedes observar, routes_out es una lista con elementos inversos respecto de routes_in. Por ende, para la mayoría de los casos será suficiente utilizar el siguiente código.

routes_out = [(y, x) for x, y in routes_in]

Este código creará automáticamente las direcciones de salida respecto de las de entrada. Nótese que este método no funciona si el patrón incluye expresiones regulares, como veremos a continuación.

Hasta el momento resultó bastante sencillo asignar una URL amigable a un controlador determinado. Sin embargo, los patrones que empleamos fueron bastante sencillos. El sistema basado en patrones de web2py permite especificar expresiones regulares para patrones más complejos. Sin embargo, para la mayoría de los casos será suficiente evitar las expresiones regulares y, en su lugar, emplear alguno de los 4 placeholders que provee el framework.

Por defecto, web2py incorpora el siguiente patrón, como vimos al comienzo del artículo.

routes_in = [
    ("/$a/$c/$f", "/$a/$c/$f")
]

En donde $a, $c y $f representan el nombre de una aplicación, un controlador y una función. Estos placeholders pueden utilizarse, por ejemplo, para omitir la aplicación y controlador por defecto.

routes_in = [
    ("/$f", "/myapp/default/$f"),
]

De esta forma, siempre que se omitan el nombre de la aplicación y el controlador, indicando únicamente el nombre de una función, se tomará por defecto la aplicación «myapp» y el controlador «default». En nuestro caso, la dirección /register es vinculada con /myapp/default/register.

El cuarto placeholder es $anything que nomina, como su nombre lo indica, cualquier cosa (similar a (.*) en una expresión regular). En el código anterior, /register?a=1 satisface el patrón especificado (/$f), pero /register/1 no lo hace. Para esto, podemos utilizar:

routes_in = [
    ("/$f", "/myapp/default/$f"),
    ("/$f/$anything", "/myapp/default/$f/$anything"),
]

También resulta útil para remover el nombre de la aplicación de la dirección de URL.

routes_in = [
    # Controlador por defecto.
    ("/$f", "/myapp/default/$f"),
    ("/$f/$anything", "/myapp/default/$f/$anything"),
    # Aplicación por defecto.
    ("/$anything", "/myapp/$anything"),
]

Con esta introducción, toda dirección de URL será asignada a la aplicación «myapp».

Nótese que el orden de los patrones es importante. web2py los ejecutará según su definición. La dirección /register satisface tanto el primer como el último patrón, pero el primero es considerado por ser justamente la primera coincidencia.

Por último, añadimos dos patrones para exponer tanto la aplicación admin como el controlador appadmin en sus direcciones correspondientes.

routes_in = [
    # Exponer admin y appadmin.
    ("/admin$anything", "/admin$anything"),
    ("/appadmin$anything", "/myapp/appadmin$anything"),
    # Controlador por defecto.
    ("/$f", "/myapp/default/$f"),
    ("/$f/$anything", "/myapp/default/$f/$anything"),
    # Aplicación por defecto.
    ("/$anything", "/myapp/$anything"),
]

Por lo comentado anteriormente, ambos patrones deben ubicarse al comienzo.

Código completo:

# -*- coding: utf-8 -*-

default_application = "myapp"

routes_in = [
    # Exponer admin y appadmin.
    ("/admin$anything", "/admin$anything"),
    ("/appadmin$anything", "/myapp/appadmin$anything"),
    # Controlador por defecto.
    ("/$f", "/myapp/default/$f"),
    ("/$f/$anything", "/myapp/default/$f/$anything"),
    # Aplicación por defecto.
    ("/$anything", "/myapp/$anything"),
]

routes_out = [(y, x) for x, y in routes_in]

Curso online 👨‍💻

¡Ya lanzamos el curso oficial de Recursos Python en Udemy! Un curso moderno para aprender Python desde cero con programación orientada a objetos, SQL y tkinter en 2024.

Consultoría 💡

Ofrecemos servicios profesionales de desarrollo y capacitación en Python a personas y empresas. Consultanos por tu proyecto.

Deja una respuesta