Quick Answer
Stop 30-minute file searches. Messy way: Files scattered (some in attachments dropdown, some in binary fields, some on broken Google Drive links). Result: "Where is invoice for Order #1234?" Organized way: Use ir.attachment, store heavy files on disk (Filestore) not database, use Documents workspaces with auto-tagging. Result: Click order → Click "Documents" smart button → See invoice/packing slip instantly. Proper architecture prevents 50GB database bloat.
The File Management Problem
Your D2C brand deals with thousands of files:
❏ Product images (high-res for Shopify)
❏ Supplier invoices (PDFs)
❏ Customer contracts (signed PDFs)
❏ Import/Export CSVs
❏ Technical specs for manufacturing
The "Messy" Way
Files are scattered everywhere. Some in the "Attachments" dropdown. Some in binary fields. Some on a Google Drive with a broken link in the Chatter.
Result: "Hey, where is the invoice for Order #1234?" [30 minutes of searching]
The "Organized" Way
Odoo handles files natively using ir.attachment. You configure it to store heavy files on disk (Filestore) instead of bloating the database. You use "Documents" workspaces to tag and route files automatically.
Result: Click Order #1234 → Click "Documents" smart button → See Invoice, Packing Slip, and Shipping Label instantly.
We've implemented 150+ Odoo systems. File management is often an afterthought until the database backup size hits 50GB and the system crawls. Proper file architecture prevents this.
The Core: ir.attachment
In Odoo, every file (attachment) is a record in the ir.attachment model. Even the "Image" field on a product card is technically linked to an attachment or stored as binary data.
Key Fields in ir.attachment
| Field | Description |
|---|---|
| name | Filename (e.g., invoice.pdf) |
| datas | The actual file content (base64 encoded) |
| res_model | The model it belongs to (e.g., sale.order) |
| res_id | The ID of the record it belongs to (e.g., Order #10) |
| type | 'binary' (stored in Odoo) or 'url' (link to external file) |
Method 1: fields.Binary (Simple Storage)
Use this when the file is a property of the record (e.g., a Product Image or a Signature).
class ProductTemplate(models.Model):
_inherit = 'product.template'
# Store a specification PDF
tech_spec = fields.Binary(string="Technical Spec PDF", attachment=True)
tech_spec_filename = fields.Char(string="Spec Filename")
Critical Parameter: attachment=True
✓ attachment=True (Recommended): Odoo creates a record in ir.attachment and stores the data there. If configured, it saves to the disk (filestore), keeping your database lean.
❌ attachment=False: Odoo stores the base64 string directly in the SQL table column. Pros: Faster for tiny icons. Cons: Bloats the database. Backups become massive. Queries slow down if you select this column.
Frontend View (XML)
Always include the filename field so Odoo knows what extension to use when downloading.
<field name="tech_spec_filename" invisible="1"/>
<field name="tech_spec" filename="tech_spec_filename"/>
Method 2: Managing ir.attachment Directly (Advanced)
Use this when you need to attach multiple files to a record (like the "Attachments" dropdown in the chatter) programmatically.
Scenario: Generating a PDF Report and attaching it to the Order automatically.
import base64
def action_generate_and_attach_report(self):
self.ensure_one()
# 1. Generate PDF (returns binary data)
pdf_content, _ = self.env.ref('sale.action_report_saleorder')._render_qweb_pdf(self.id)
# 2. Encode to Base64
b64_pdf = base64.b64encode(pdf_content)
# 3. Create Attachment
attachment = self.env['ir.attachment'].create({
'name': f"Order_{self.name}.pdf",
'type': 'binary',
'datas': b64_pdf,
'res_model': 'sale.order',
'res_id': self.id,
'mimetype': 'application/pdf'
})
# 4. Post to Chatter (Optional)
self.message_post(
body="Order Confirmation generated.",
attachment_ids=[attachment.id]
)
Storage: Database vs. Filestore
Where do the files actually go?
Default Behavior
Odoo stores file content in the Database (PostgreSQL) in the datas column.
Problem: Postgres is great for data, bad for BLOBs (Binary Large Objects). A 10GB database backup takes forever to dump/restore.
Solution: The Filestore
You configure Odoo to store the actual file content on the Server's File System (Disk), while the Database only keeps a pointer (SHA1 hash) to the file.
How to Enable
This is usually enabled by default in Odoo.sh and standard installs. Check your odoo.conf:
data_dir = /var/lib/odoo
If configured, Odoo saves attachments in /var/lib/odoo/filestore/DB_NAME/.
Retrieving Files (Serving to Users)
If you need to give a URL to a user (e.g., "Click here to download the manual"), use the /web/content controller.
Dynamic URL Pattern
/web/content/{attachment_id}/{filename}
Or by Model/ID (Better): /web/content/sale.order/{order_id}/tech_spec/{filename}
Example in Email Template
<p>Download your manual here:
<a t-attf-href="/web/content/product.template/{{object.product_id.id}}/tech_spec/manual.pdf">
Download PDF
</a>
</p>
Organizing with Odoo Documents (Enterprise)
If you have Odoo Enterprise, the Documents app is a game changer. It adds a layer of "Workspaces," "Tags," and "Actions" on top of standard attachments.
Key Feature: Spreadsheet Integration
You can create a "Finance" workspace where all vendor bill PDFs automatically go. The OCR reads them, and you export the data to an Odoo Spreadsheet for analysis.
Key Feature: Smart Folders
You can configure a workspace to "Show all attachments linked to Sales Orders." You don't move the files; it's a virtual view.
Best Practices for D2C Brands
✓ Compress Images: Don't upload 5MB raw product photos. Use a tool (or Odoo module) to compress them to web-optimized JPEGs (< 200KB).
✓ Use attachment=True: Always. Keep your Postgres DB light.
✓ Naming Conventions: Don't save files as scan001.pdf. Use automation to rename them Invoice_INV2023_001.pdf upon creation.
✓ Security: Remember that ir.attachment uses Access Rules. If a user cannot read sale.order, they typically cannot read the attachments linked to it.
Storage Comparison
| Aspect | Database Storage | Filestore (Disk) |
|---|---|---|
| Backup Size | Huge (includes all file content) | Small (DB only has pointers) |
| Performance | Slow for large files | Fast (disk I/O optimized) |
| Restore Speed | Very slow | Fast |
| Use Case | Tiny icons only | All files (recommended) |
Action Items: Optimize File Storage
Check Your Storage
❏ Go to Settings → Technical → Database Structure → Attachments
❏ Group by "File Size". Are there massive files clogging your system?
❏ Check if store_fname is populated (means it's on disk) or empty (means it's in DB)
Clean Up
❏ Archive or delete old export CSVs generated by wizards
❏ Compress huge product images
Automate
❏ Implement the Python script to auto-generate and attach PDFs for your key workflows
Frequently Asked Questions
Should I use attachment=True or attachment=False for Binary fields?
Always use attachment=True. This creates an ir.attachment record and stores files on disk (filestore) if configured, keeping your PostgreSQL database lean. attachment=False stores base64 strings directly in SQL columns, bloating the database and slowing backups.
What's the difference between Database storage and Filestore?
Database: Stores file content in PostgreSQL (slow backups, bloated DB). Filestore: Stores files on server disk at /var/lib/odoo/filestore/, database only keeps SHA1 pointer (fast backups, lean DB). Configure via data_dir = /var/lib/odoo in odoo.conf.
How do I programmatically attach a PDF to an order?
Generate PDF, encode to base64, create ir.attachment record with name, type='binary', datas (base64), res_model='sale.order', res_id=order.id. Optionally post to chatter with message_post(attachment_ids=[attachment.id]).
How do I prevent my database from bloating to 50GB?
Enable Filestore (data_dir in odoo.conf), always use attachment=True for Binary fields, compress product images to <200KB, delete old export CSVs, check store_fname field in attachments to verify files are on disk, not in database.
Free Infrastructure Review
Is your backup taking 4 hours? Is your Odoo slow loading images? We'll analyze your database vs. filestore usage, optimize your Postgres bloat, implement automated image compression, and structure your Odoo Documents workspace for maximum efficiency. Don't let file bloat kill your ERP performance.
