Odoo · Website · eCommerce
Personalizar el website de Odoo sin romper el tema es cuestión de herencia, no de reescritura. Editar plantillas con replace agresivo, mezclar cambios de tema con datos de la web o tocar vistas del core son las tres vías rápidas a un sitio que se rompe en cada actualización. Veamos cómo extender bien.
Conceptos
Tema vs módulo: qué va dónde
| Necesitas… | Lócalo en… |
|---|---|
| Colores, fuentes, layout base | Módulo tema (theme_*) con theme.ir.ui.view |
| Bloque reutilizable arrastrable | Snippet (plantilla + opciones) |
| Cambio funcional en una página | Vista heredada en tu módulo de negocio |
| Página nueva con datos del ORM | Controlador + plantilla QWeb |
Regla de oro: nunca edites una vista del core directamente. Hereda con inherit_id y xpath puntuales.
Herencia
Extender una plantilla del website
<template id="product_extra_info"
inherit_id="website_sale.product"
name="Producto: ficha técnica">
<xpath expr="//div[@id='product_details']" position="inside">
<div t-if="product.datasheet_url" class="mt-3">
<a t-att-href="product.datasheet_url"
class="btn btn-outline-secondary">
Descargar ficha técnica
</a>
</div>
</xpath>
</template>
customize_show="True" y active="False" si quieres que el usuario active/desactive tu extensión desde el editor del website (menú Personalizar).
Snippets
Un snippet arrastrable
<!-- Plantilla del snippet -->
<template id="s_cta_diagnostic" name="CTA Diagnóstico">
<section class="s_cta_diagnostic py-5 text-center">
<h2>¿Reservar una cita?</h2>
<a href="/contactus" class="btn btn-primary">Contactar</a>
</section>
</template>
<!-- Registro en el panel de bloques -->
<template id="snippet_cta_register"
inherit_id="website.snippets">
<xpath expr="//snippets[@id='snippet_structure']" position="inside">
<t t-snippet="mi_modulo.s_cta_diagnostic"
t-thumbnail="/mi_modulo/static/img/cta.svg"/>
</xpath>
</template>
El class del section (s_cta_diagnostic) identifica el snippet para sus opciones y para el editor. Mantenlo único por módulo.
Páginas dinámicas
Controlador + QWeb con datos del ORM
from odoo import http
from odoo.http import request
class DiagnosticWebsite(http.Controller):
@http.route('/diagnosticos', type='http', auth='public',
website=True, sitemap=True)
def diagnostic_list(self, **kw):
tests = request.env['diagnostic.test'].sudo().search(
[('website_published', '=', True)], limit=20)
return request.render('mi_modulo.page_diagnostics', {
'tests': tests,
})
auth='public' el usuario es el portal anónimo. Si usas sudo(), filtra siempre por un campo tipo website_published para no exponer registros privados. Nunca devuelvas todo el modelo.
Buenas prácticas
Qué evitar para no romper actualizaciones
| ❌ Evitar | ✅ Preferir |
|---|---|
position="replace" de bloques grandes | inside / after puntuales |
| Editar vistas del core en la BD | Vista heredada en tu módulo |
| CSS inline en cada plantilla | SCSS en el bundle web.assets_frontend |
| Mezclar tema y lógica de negocio | Tema en theme_*, funcional aparte |
Resumen
Separa el tema (apariencia) de tu módulo (funcionalidad). Hereda plantillas con xpath puntuales, registra snippets reutilizables y sirve páginas dinámicas con controladores website=True filtrando siempre los datos públicos. Así sobrevives a cada -u website sin sorpresas.