Trabajando con monedas y precios en web2py

Si bien web2py no provee por defecto un tipo de dato para divisas, fácilmente podemos desarrollar uno para almacenarlo en una base de datos y presentarlo en formularios.

Como ejemplo estaremos utilizando una aplicación muy simple que permita añadir y visualizar productos de una tabla. Cada producto tendrá únicamente un título o nombre y un precio.

Formulario con moneda en web2py

Como casi todas las cosas en programación, probablemente haya infinidad de métodos para implementar un campo de precio con su respectiva moneda en web2py. Aquí expongo una solución que considero sencilla y amigable con el framework.

La idea es crear un campo para seleccionar una divisa (Dólar, Euro o Libra, por ejemplo) y otro para indicar el precio.

Comenzaremos por definir el modelo de nuestra tabla product en db.py o cualquier otro archivo dentro de la carpeta models.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Base de datos SQLite.
db = DAL("sqlite://storage.db")

# Tabla para productos.
db.define_table(
    "product",
    Field(
        "name",
        length=100,
        label="Nombre del producto"
    ),
    Field(
        "price_currency",
        type="integer",
        label="Moneda"
    ),
    Field(
        "price",
        type="decimal(9, 3)",
        label="Precio"
    )
)

Así, nuestra tabla queda definida con tres campos: name, una cadena de no más de 100 caracteres; price_currency, que almacenará el tipo de divisa; y price, que indicará el monto.

Nótese que price_currency es un entero, ya que estaremos utilizando un identificador numérico para cada una de las divisas (esto nos facilitará las cosas más adelante).

price es del tipo decimal con una precisión de hasta 3 números después de la coma y, como máximo, 9 dígitos en total (esto deberías configurarlo según tus necesidades).

Dentro de la carpeta modules crearemos un nuevo módulo de nombre currency.py para añadir todo lo relacionado con este nuevo tipo de dato. Como mencionaba anteriormente, empezaremos por identificar a cada una de las divisas con un valor numérico.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# currency.py
#

DOLLAR = 1
EURO = 2
POUND_STERLING = 3

Y, a continuación, le asignaremos a cada moneda su respectiva representación textual.

symbols = {
    DOLLAR: "USD",
    EURO: "EUR",
    POUND_STERLING: "GBP"
}

Con esta simple estructura, podemos añadir nuevas divisas en el futuro o bien cambiar la representación textual sin hacer cambios en la aplicación o la base de datos: simplemente editando el archivo currency.py.

Volviendo a nuestro modelo, db.py, al comienzo del archivo importaremos el nuevo módulo:

import currency

Y añadiremos el validador IS_IN_SET al campo price_currency para que los formularios muestren una lista de opciones con las divisas disponibles.

    Field(
        "price_currency",
        type="integer",
        requires=IS_IN_SET(currency.symbols),
        label="Moneda"
    ),

Ahora bien, para desplegar un formulario que permita insertar productos, nos dirigimos al controlador default.py y definimos la función index.

def index():
    form = SQLFORM(db.product)
    form.process()
    return dict(form=form)

Y la vista views/default/index.html debería verse más o menos así:

{{extend "layout.html"}}
<div style="max-width:400px;">
  {{=form}}
</div>

Si el nombre de nuestra aplicación es currencyapp, en la dirección http://127.0.0.1:8000/currencyapp/default/index deberíamos ver el formulario para añadir un producto.

Para obligar al usuario a ingresar un nombre y un precio, podemos añadir los validadores IS_NOT_EMPTY a los campos name y price.

db.define_table(
    "product",
    Field(
        "name",
        length=100,
        requires=IS_NOT_EMPTY(),
        label="Nombre del producto"
    ),
    Field(
        "price_currency",
        type="integer",
        requires=IS_IN_SET(currency.symbols),
        label="Moneda"
    ),
    Field(
        "price",
        type="decimal(9, 3)",
        requires=IS_NOT_EMPTY(),
        label="Precio"
    )
)

Además, añadiremos en el controlador la función browse que desplegará una lista con todos los productos en la base de datos.

def browse():
    grid = SQLFORM.grid(db.product, csv=False, searchable=False)
    response.view = "views/default/index.html"
    return dict(form=grid)

Una vez agregados algunos productos, visitando http://127.0.0.1:8000/currencyapp/default/browse la vista debería similar a la siguiente.

Tabla de productos en web2py

El problema que se hace visible rápidamente es que la tabla muestra los identificadores numéricos de las divisas. Por otro lado, lo ideal sería tener una única columna para el precio en lugar de dos. Para resolver esto, empezaremos por ocultar la columna «Moneda» utilizando readable=False.

# db.py
    Field(
        "price_currency",
        type="integer",
        requires=IS_IN_SET(currency.symbols),
        readable=False,
        label="Moneda"
    ),

Y para el segundo caso, haremos que la columna «Precio» incluya el signo o representación de la moneda junto al valor numérico, haciendo uso del parámetro represent.

    Field(
        "price",
        type="decimal(9, 3)",
        requires=IS_NOT_EMPTY(),
        represent=lambda value, row: "{} {}".format(
            currency.symbols[row.price_currency], value),
        label="Precio"
    )

Hecho esto, el resultado es el siguiente:

Tabla de productos con precios en web2py

¡Mucho más agradable!

Puedes descargar el código completo de la aplicación de web2py desde este enlace.

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