Usar el ORM para acceder a la información dentro de Odoo es una buena práctica. Y para de contar. Muchas veces cuando las papas queman en términos de performance, nos vemos obligados a acceder directamente a la base de datos para obtener los datos mediante SQL. Lo cual no es mala idea ya que nos permite obtener ganancias de performance de por lo menos dos a diez veces más rápido.
A que se debe esto? Cada vez que realizamos una llamada del ORM, Odoo termina haciendo esto:
- Crear los objetos del recorset
- Chequea seguridad, recomputa campos leidos
- Actualiza el cache
- Convierte las filas leídas en objetos de Python
Lo cual esta bien si estamos trabajando con decenas de registros, pero si estamos trabajando con centenares o miles de registros (como sucede con clientes o productos) estamos en problemas.
Para ello conviene acceder a los datos directamente mediante SQL. Una forma facil es hacerlo con campos computados, por ejemplo como hacemos a continuación:
cs_avg_qty_orders = fields.Integer('cantidad de pedidos por mes',compute='_compute_cs_avg_qty_orders')
cs_stddev_qty_orders = fields.Integer('Desvio standard de pedidos por mes',compute='_compute_cs_stddev_qty_orders')
cs_qty_orders = fields.Integer('cantidad de pedidos (kgs)',compute='_compute_cs_qty_unit_orders')
En donde para cada uno de los campos (cantidad, promedio y desvio standard) definimos un campo computado con su correspondiente método. Y los métodos son los siguientes
def _compute_cs_qty_orders(self):
for rec in self:
self.env.cr.execute("""
SELECT count(*)
FROM sale_order
WHERE partner_id = %s AND credit_scoring = TRUE
AND date_order >= (CURRENT_DATE - INTERVAL '4 months')
""", [rec.id])
rec.cs_qty_orders = self.env.cr.fetchone()[0] or 0
def _compute_cs_avg_qty_orders(self):
for rec in self:
self.env.cr.execute("""
SELECT AVG(order_count)
FROM (
SELECT DATE_TRUNC('month', date_order) AS month, COUNT(*) AS order_count
FROM sale_order
WHERE date_order IS NOT NULL
AND partner_id = %s AND credit_scoring = TRUE
AND date_order >= (CURRENT_DATE - INTERVAL '4 months')
GROUP BY DATE_TRUNC('month', date_order)
) sub;
""", [rec.id])
rec.cs_avg_qty_orders = self.env.cr.fetchone()[0] or 0
def _compute_cs_stddev_qty_orders(self):
for rec in self:
self.env.cr.execute("""
SELECT STDDEV_POP(order_count)
FROM (
SELECT DATE_TRUNC('month', date_order) AS month, COUNT(*) AS order_count
FROM sale_order
WHERE date_order IS NOT NULL
AND partner_id = %s AND credit_scoring = TRUE
AND date_order >= (CURRENT_DATE - INTERVAL '4 months')
GROUP BY DATE_TRUNC('month', date_order)
) sub;
""", [rec.id])
rec.cs_stddev_qty_orders = self.env.cr.fetchone()[0] or 0
Como podran ver, Odoo permite ejecutar dentro de un método una consulta de SQL (ya sea para consultar o actualizar datos). Solo hace falta saber definir el SQL (no es tan dificil aprenderlo), y al resultado de la consulta, asignarlo al campo computado. No es el fin del mundo.
Por último; tengan en cuenta que si bien el ORM de Odoo esta optimizado para performance; PostgreSQL es muchísimo más rápido. Como pueden ver en los ejemplos que acabamos de mostrar, en PostgreSQL estan ejecutandose sumarizaciones y funciones estadísticas como promedios y desvíos estandard.