¿CÓMO CARGAR ARCHIVOS DESDE UN FORMULARIO WEB EN ODOO 19?
Los formularios web se utilizan ampliamente en sitios web para recopilar información de los usuarios, como consultas, documentos o solicitudes de servicios. En muchos escenarios, los usuarios también necesitan la posibilidad de cargar archivos junto con sus envíos. Odoo 19 proporciona un marco flexible para manejar este requisito utilizando controladores, modelos y vistas de sitios web.
En este blog explicaremos cómo crear un módulo Web Form Attachment en Odoo 19 que permite a los usuarios cargar archivos desde un formulario de sitio web público. Los archivos cargados se almacenan de forma segura en el backend de Odoo y los usuarios internos pueden administrarlos a través de listas estándar y vistas de formularios.
Este módulo utiliza componentes estándar de Odoo, como modelos, controladores, plantillas de sitios web y reglas de seguridad. Es liviano, fácil de entender y adecuado para aprender o ampliar la funcionalidad de carga de archivos en aplicaciones comerciales reales.
Estructura del directorio
Plaintext
web_form_attachment/
+-- __init__.py
+-- __manifest__.py
+-- controllers/
¦ +-- __init__.py
¦ +-- main.py
+-- models/
¦ +-- __init__.py
¦ +-- form_attachment.py
+-- security/
¦ +-- ir.model.access.csv
+-- views/
+-- templates.xml
+-- form_attachment_views.xml
Archivo de Manifiesto
El archivo de manifiesto define los metadatos, dependencias y archivos de datos del módulo. Le dice a Odoo cómo y cuándo cargar el módulo.
manifest.py:
Python
{
'name': 'Web Form Attachment',
'version': '19.0.1.0.0',
'category': 'Extra Tools',
'summary': 'Allows users to upload files from a web form',
'description': """
Este módulo agrega un formulario web donde los usuarios pueden cargar archivos,
que luego se almacenan como archivos adjuntos en el backend.
""",
'website': 'https://www.cybrosys.com',
'depends': ['base', 'web', 'website'],
'data': [
'security/ir.model.access.csv',
'views/form_attachment_views.xml',
'views/templates.xml',
],
'installable': True,
'application': True,
'auto_install': False,
}
Menú del Sitio Web
Para que el formulario de carga de archivos sea accesible, agregamos un elemento de menú personalizado al encabezado del sitio web heredando la plantilla predeterminada.
views/templates.xml:
XML
<template id="website_menu_form" inherit_id="website.template_header_default" name="Website Menu Form">
<xpath expr="//t[@t-foreach='website.menu_id.child_id']" position="after">
<li class="nav-item">
<a class="nav-link" href="/form">
<span>Upload File</span>
</a>
</li>
</xpath>
</template>
Controlador del Sitio Web
El controlador gestiona tanto la visualización del formulario como el manejo de los datos enviados. La primera ruta (/form) representa el formulario y la segunda ruta (/form/submit) procesa el archivo cargado.
controllers/main.py:
Python
# -*- coding: utf-8 -*-
import base64
from odoo import http
from odoo.http import request
class WebForm(http.Controller):
@http.route('/form', type='http', auth='public', website=True)
def web_form(self, **kw):
""" Renderizar la plantilla del formulario """
return request.render('web_form_attachment.web_form_template', {})
@http.route('/form/submit', type='http', auth='public', website=True, csrf=False)
def web_form_submit(self, **kw):
if kw.get('attachment'):
attachment = kw.get('attachment').read()
request.env['form.attachment'].sudo().create({
'name': kw.get('name'),
'attachment': base64.b64encode(attachment),
'attachment_filename': kw.get('attachment').filename,
})
return request.render(
'web_form_attachment.web_form_thank_you_template', {}
)
return self.web_form()
Plantilla del Formulario
La plantilla define la interfaz de usuario con campos para descripción y archivo, utilizando multipart/form-data para permitir la carga.
views/templates.xml (continuación):
XML
<template id="web_form_template" name="Web Form">
<t t-call="website.layout">
<div id="wrap">
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3">
<h1 class="text-center">Upload a File</h1>
<form action="/form/submit" method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<div class="form-group">
<label for="name">Description</label>
<input type="text" class="form-control"
id="name" name="name" required="1"/>
</div>
<div class="form-group">
<label for="attachment">File</label>
<input type="file" class="form-control-file"
id="attachment" name="attachment" required="1"/>
</div>
<button type="submit" class="btn btn-primary">
Submit
</button>
</form>
</div>
</div>
</div>
</div>
</t>
</template>
Modelo Backend
El modelo form.attachment actúa como almacenamiento central para todos los archivos cargados, guardando la descripción y el binario del archivo.
models/form_attachment.py:
Python
# -*- coding: utf-8 -*- from odoo import fields, models class FormAttachment(models.Model): _name = 'form.attachment' _description = 'Form Attachment' name = fields.Char(string='Description', required=True) attachment = fields.Binary(string='Attachment', required=True) attachment_filename = fields.Char(string='Attachment Filename')
Seguridad y Permisos
El archivo ir.model.access.csv otorga a los usuarios internos los derechos necesarios para gestionar los registros.
security/ir.model.access.csv:
Fragmento de código
access_form_attachment_user,form.attachment.user,model_form_attachment,base.group_user,1,1,1,1
Página de Agradecimiento
Una plantilla simple para confirmar al usuario que la carga fue exitosa.
views/templates.xml (continuación):
XML
<template id="web_form_thank_you_template" name="Thank You">
<t t-call="website.layout">
<div id="wrap">
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3">
<h1 class="text-center">Thank You!</h1>
<p class="text-center">Your file has been uploaded successfully.</p>
</div>
</div>
</div>
</div>
</t>
</template>
Vistas y Menú Backend
Finalmente, configuramos las vistas de lista y formulario en el backend para que los usuarios internos puedan acceder a los archivos cargados.
views/form_attachment_views.xml:
XML
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="form_attachment_view_list" model="ir.ui.view">
<field name="name">form.attachment.view.list</field>
<field name="model">form.attachment</field>
<field name="arch" type="xml">
<tree string="Form Attachments">
<field name="name"/>
<field name="attachment_filename"/>
</tree>
</field>
</record>
<record id="form_attachment_view_form" model="ir.ui.view">
<field name="name">form.attachment.view.form</field>
<field name="model">form.attachment</field>
<field name="arch" type="xml">
<form string="Form Attachment">
<sheet>
<group>
<field name="name"/>
<field name="attachment" widget="binary" filename="attachment_filename"/>
<field name="attachment_filename" invisible="1"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="form_attachment_action" model="ir.actions.act_window">
<field name="name">Form Attachments</field>
<field name="res_model">form.attachment</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="form_attachment_menu_root" name="Web Form Attachments" sequence="10"/>
<menuitem id="form_attachment_menu"
name="Attachments"
parent="form_attachment_menu_root"
action="form_attachment_action"
sequence="10"/>
</odoo>
Conclusión
En este blog, mostramos cómo cargar archivos desde un formulario de sitio web en Odoo 19 utilizando un enfoque simple y claro. Cubrimos el flujo completo, comenzando por crear el módulo y el menú del sitio web, crear el formulario de carga, manejar el envío con un controlador y almacenar los archivos en un modelo backend. Esta implementación sigue las mejores prácticas de Odoo y se puede ampliar fácilmente.