Introduction
File upload functionality is essential for many business applications, allowing users to attach documents directly to records. In Odoo 18, implementing a file upload feature through website forms is straightforward when you understand the key components involved. This guide walks through a complete implementation that lets users upload files and attach them to partner records.
Implementation Overview
The solution consists of these key components:
1. Model extension to store attachments
2. View modification to display attachments
3. Website form template with upload capability
4. Controller to handle form submissions
5. Module initialization files
The Complete Code and Explanation
1. Model Extension (res_partner.py)
from odoo import models, fields
class ResPartner(models.Model):
_inherit = 'res.partner'
file_attachment_ids = fields.Many2many(
'ir.attachment',
string='Attachments'
)
This code extends the standard partner model to add a many-to-many relationship with attachments. The file_attachment_ids field will store all files attached to a partner.
2. Partner View Modification (partner_views.xml)
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_partner_form_inherit" model="ir.ui.view">
<field name="name">res.partner.form.inherit</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='category_id']" position="after">
<field name="file_attachment_ids" widget="many2many_binary"/>
</xpath>
</field>
</record>
</odoo>
This XML adds our attachments field to the partner form view, positioned after the category field. The many2many_binary widget provides a user-friendly interface for file management.
3. Website Form Template (website_form.xml)
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="website_partner_form" name="Partner File Upload Form">
<t t-call="website.layout">
<div id="wrap" class="oe_structure oe_empty">
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3 mt-4 mb-4">
<div class="s_website_form_container">
<form t-attf-action="/form/submit"
method="post" role="form"
enctype="multipart/form-data">
<input type="hidden" name="csrf_token"
t-att-value="request.csrf_token()"/>
<div class="form-group row">
<label class="col-md-3 col-form-label"
for="att">Attach file
</label>
<div class="col-md-9">
<input type="file" name="att"
class="form-control"/>
</div>
</div>
<div class="form-group row">
<div class="col-md-9 offset-md-3">
<button type="submit"
class="btn btn-primary">
Create
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</t>
</template>
</odoo>
This template creates a responsive form with file upload capability, including CSRF protection and proper encoding for file uploads.
4. Controller (main.py)
import base64
from odoo import http
from odoo.http import request
class FileUploadController(http.Controller):
@http.route('/partner/form', type='http', auth='public', website=True)
def partner_form(self, **kw):
"""Render the partner form template"""
return request.render('website_file_upload_test.website_partner_form')
@http.route('/form/submit', type='http', auth='public', website=True,
csrf=True)
def form_submit(self, redirect=None, **kw):
"""Handle form submission with file attachment"""
current_partner = request.env.user.partner_id
# Get the uploaded file
uploaded_file = kw.get('att')
if uploaded_file:
# Create the attachment
attachment = request.env['ir.attachment'].create({
'name': uploaded_file.filename,
'type': 'binary',
'datas': base64.b64encode(uploaded_file.read()),
'res_model': 'res.partner',
'res_id': current_partner.id
})
# Link the attachment to the partner
current_partner.write({
'file_attachment_ids': [(4, attachment.id)],
})
return request.redirect('/partner/form')
The controller handles both displaying the form and processing submissions, including file encoding and attachment creation.
5. Module Initialization Files
init.py (root)
from . import models
from . import controllers
This initialization file imports all Python modules that need to be loaded when the module starts.
models/init.py
from . import res_partner
controllers/init.py
from . import main
These subdirectory init files ensure all model and controller files are properly loaded.
6. Module Manifest (manifest.py)
{
'name': 'Website File Upload Test',
'version': '18.0.1.0.0',
'category': 'Website',
'summary': 'Test file upload feature via website form',
'description': 'Module to upload and attach files to partner records through website form.',
'author': 'Renu M',
'depends': ['website', 'contacts','base'],
'data': [
'views/website_form.xml',
'views/partner_views.xml',
],
'installable': True,
'application': False,
'auto_install': False,
}
The manifest declares module metadata, dependencies, and data files to load.
Implementation Results


The module successfully implements a file upload feature in Odoo 18, allowing users to attach files to partner records through a website form. When submitted, files are securely encoded, stored as attachments, and linked to the partner.
Conclusion
This implementation demonstrates a robust and secure approach to handling file uploads in Odoo 18 through website forms. The solution effectively combines Odoo's built-in attachment system with custom website form handling to create a seamless file upload experience. By extending the partner model, creating a dedicated upload form, and implementing proper controller logic, we've established a pattern that can be adapted for various file attachment needs across different models. The inclusion of CSRF protection and proper file encoding ensures security, while the modular structure follows Odoo best practices for maintainability. This approach not only solves the immediate need for partner file attachments but also provides a template that can be extended with additional features like file validation, multiple uploads, or custom processing logic for specific business requirements. The complete implementation from model to view demonstrates how Odoo's framework elegantly handles file management while maintaining data integrity and security.
To read more about How to Attach a File Using the Upload Button in Odoo 17, refer to our blog How to Attach a File Using the Upload Button in Odoo 17.