TIP: You can type at any time to perform a new search.
Base Attachment Object Store
fs_attachment · OCA/storage
- Repository
- OCA/storage · module folder · Try on Runboat
- Module version
- 1.1.2
- Category
- Knowledge Management
- Folder size
- 0.34 MB
- License
- AGPL-3
- Application
- No
- Auto-installable
- No
- Website
- https://github.com/OCA/storage
- Last tracking update
- 2026-08-07 09:06:26
- Authors
- Camptocamp, ACSONE SA/NV, Odoo Community Association (OCA)
- Maintainers
- Camptocamp, ACSONE SA/NV, Odoo Community Association (OCA)
- Committers
- Stéphane Bidoul, Laurent Mignon (ACSONE), Iván Todorovich, Weblate, OCA-git-bot, oca-ci
- Odoo dependencies
- Python dependencies
- fsspec>=2025.3.0, python_slugify, fsspec>=2024.5.0
- System dependencies
- None
- Required by
- fs_attachment_s3, fs_file
- Description
In some cases, you need to store attachment in another system that the Odoo's filestore. For example, when your deployment is based on a multi-server architecture to ensure redundancy and scalability, your attachments must be stored in a way that they are accessible from all the servers. In this way, you can use a shared storage system like NFS or a cloud storage like S3 compliant storage, or.... This addon extend the storage mechanism of Odoo's attachments to allow you to store them in any storage filesystem supported by the Python library [fsspec](https://filesystem-spec.readthedocs.io/en/latest/) and made available via the fs_storage addon. In contrast to Odoo, when a file is stored into an external storage, this addon ensures that the filename keeps its meaning (In odoo the filename into the filestore is the file content checksum). Concretely the filename is based on the pattern: '\<name-without-extension\>-\<attachment-id\>-\<version\>.\<extension\>' This addon also adds on the attachments 2 new fields to use to retrieve the file content from a URL: - `Internal URL`: URL to retrieve the file content from the Odoo's filestore. - `Filesystem URL`: URL to retrieve the file content from the external storage. Note The internal URL is always available, but the filesystem URL is only available when the attachment is stored in an external storage. Particular attention has been paid to limit as much as possible the consumption of resources necessary to serve via Odoo the content stored in an external filesystem. The implementation is based on an end-to-end streaming of content between the external filesystem and the Odoo client application by default. Nevertheless, if your content is available via a URL on the external filesystem, you can configure the storage to use the x-sendfile mechanism to serve the content if it's activated on your Odoo instance. In this case, the content served by Odoo at the internal URL will be proxied to the filesystem URL by nginx. Last but not least, the addon adds a new method open on the attachment. This method allows you to open the attachment as a file. For attachments stored into the filestore or in an external filesystem, it allows you to directly read from and write to the file and therefore minimize the memory consumption since data are not kept into memory before being written into the database.
Code Analysis ⓘ
Views touched (1)
| XML ID | Name | Model | Type | Status |
|---|---|---|---|---|
fs_storage_form_view |
fs.storage.form (in fs_attachment) | fs.storage | form | Inherits fs_storage.fs_storage_form_view |
HTTP endpoints (0)
No HTTP endpoints found for this module.
Models touched (4)
New fields (2)
-
fs_storage_codeCharargs: 'Storage Code' -
store_fnameCharargs: 'Stored Filename'
No public methods.
New fields (9)
-
autovacuum_gcBooleandefault=Truehelp='If checked, the autovacuum of the garbage collection will be automatically executed when the storage is used to store attachments. Sometime, the autovacuum is to avoid when files in the storage are referenced by other systems (like a website). In such case, records in the fs.file.gc table must be manually processed.'string='Autovacuum Garbage Collection' -
base_urlChardefault='' -
base_url_for_filesCharcompute='_compute_base_url_for_files'store=True -
force_db_for_default_attachment_rulesTextdefault=<expr>help='When storing attachments in an external storage, storage may be slow.If the storage is used to store odoo attachments by default, this could lead to a bad user experience since small images (128, 256) are used in Odoo in list / kanban views. We want them to be fast to read.This field allows to force the store of some attachments in the odoo database. The value is a dict Where the key is the beginning of the mimetype to configure and the value is the limit in size below which attachments are kept in DB. 0 means no limit.\nDefault configuration means:\n* images mimetypes (image/png, image/jpeg, ...) below 50KB are stored in database\n* application/javascript are stored in database whatever their size \n* text/css are stored in database whatever their size' -
is_directory_path_in_urlBooleandefault=Falsehelp='Normally the directory_path is for internal usage. If this flag is enabled the path will be used to compute the public URL.' -
optimizes_directory_pathBooleanhelp="If checked, the directory path will be optimized to avoid too much files into the same directory. This options is used when the storage is used to store attachments. Depending on the storage, this option can be ignored. It's useful for storage based on real file. This way, files with similar properties will be stored in the same directory, avoiding overcrowding in the root directory and optimizing access times." -
use_as_default_for_attachmentsBooleandefault=Falsehelp='If checked, this storage will be used to store all the attachments ' -
use_filename_obfuscationBooleanhelp='If checked, the filename will be obfuscated. This option is useful to avoid to expose sensitive information trough the URL or in the remote storage. The obfuscation is done using a hash of the filename. The original filename is stored in the attachment metadata. The obfusation is to avoid if the storage is used to store files that are referenced by other systems (like a website) where the filename is important for SEO.' -
use_x_sendfile_to_serve_internal_urlBooleanhelp="If checked and odoo is behind a proxy that supports x-sendfile, the content served by the attachment's internal URL will be servedby the proxy using the fs_url if defined. If not, the file will be served by odoo that will stream the content read from the filesystem storage. This option is useful to avoid to serve files from odoo and therefore to avoid to load the odoo process. "string='Use X-Sendfile To Serve Internal Url'
-
create(self, vals_list)@api.model_create_multi -
get_default_storage_code_for_attachments(self)@api.modelReturn the code of the storage to use to store the attachments. If the resource field is linked to a particular storage, return this one. Otherwise if the resource model is linked to a particular storage, return it. Finally return the code of the storage to use by default. -
get_force_db_for_default_attachment_rules(self, code)@api.model@tools.ormcache('code')Return the rules to force the storage of some attachments in the DB :param code: the code of the storage :return: a dict where the key is the beginning of the mimetype to configure and the value is the limit in size below which attachments are kept in DB. 0 means no limit. -
get_storage_code_for_attachments_fallback(self)@api.model@tools.ormcache() -
recompute_urls(self) -> NoneRecompute the URL of all attachments since the base_url or the directory_path has changed. This method must be explicitly called by the user since we don't want to recompute the URL on each change of the base_url or directory_path. We could also have cases where such a recompute is not wanted. For example, when you restore a database from production to staging, you don't want to recompute the URL of the attachments created in production (since the directory_path use in production is readonly for the staging database) but you change the directory_path of the staging database to ensure that all the moditications in staging are done in a different directory and will not impact the production. -
write(self, vals)
New fields (6)
-
fs_filenameCharhelp='The name of the file in the filesystem storage.To preserve the mimetype and the meaning of the filenamethe filename is computed from the name and the extension'readonly=True args: 'File Name into the filesystem storage' -
fs_storage_codeCharrelated='fs_storage_id.code'store=True args: 'Filesystem Storage Code' -
fs_storage_idMany2one → fs.storagecompute='_compute_fs_storage_id'help='The storage where the file is stored.'ondelete='restrict'store=True args: 'fs.storage', 'Filesystem Storage' -
fs_urlCharcompute='_compute_fs_url'help='The URL to access the file from the filesystem storage.'store=True args: 'Filesystem URL' -
fs_url_pathCharcompute='_compute_fs_url_path'help='The path to access the file from the filesystem storage.' args: 'Filesystem URL Path' -
internal_urlCharcompute='_compute_internal_url'help='The URL to access the file from the server.' args: 'Internal URL'
-
create(self, vals_list)@api.model_create_multiStorage may depend on resource field, but the method calling _storage (_get_datas_related_values) does not take all vals, just the mimetype. The only way to give res_field and res_model to _storage method is to pass them into the context, and perform 1 create call per record to create. -
force_storage(self)@api.model -
force_storage_to_db_for_special_fields(self, new_cr=False, storage: <expr>=None)@api.modelMigrate special attachments from Object Storage back to database The access to a file stored on the objects storage is slower than a local disk or database access. For attachments like image_small that are accessed in batch for kanban views, this is too slow. We store this type of attachment in the database. This method can be used when migrating a filestore where all the files, including the special files (assets, image_small, ...) have been pushed to the Object Storage and we want to write them back in the database. It is not called anywhere, but can be called by RPC or scripts. -
open(self, mode='rb', block_size=None, cache_options=None, compression=None, new_version=True, **kwargs) -> io.IOBaseReturn a file-like object from the filesystem storage where the attachment content is stored. In read mode, this method works for all attachments, even if the content is stored in the database or into the odoo filestore or a filesystem storage. The resultant instance must function correctly in a context ``with`` block. (parameters are ignored in the case of the database storage). Parameters ---------- path: str Target file mode: str like 'rb', 'w' See builtin ``open()`` block_size: int Some indication of buffering - this is a value in bytes cache_options : dict, optional Extra arguments to pass through to the cache. compression: string or None If given, open file using compression codec. Can either be a compression name (a key in ``fsspec.compression.compr``) or "infer" to guess the compression from the filename suffix. new_version: bool If True, and mode is 'w', create a new version of the file. If False, and mode is 'w', overwrite the current version of the file. This flag is True by default to avoid data loss and ensure transaction mechanism between Odoo and the filesystem storage. encoding, errors, newline: passed on to TextIOWrapper for text mode Returns ------- A file-like object TODO if open with 'w' in mode, we could use a buffered IO detecting that the content is modified and invalidating the attachment cache... -
write(self, vals)
New fields (0)
No new fields.
Public methods (0)No public methods.
Loading…
Loading…
Loading…