Base Attachment Object Store

fs_attachment
REPOSITORY
REPOSITORYOCA/storage
GIT
GIThttps://github.com/OCA/storage.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/storage/tree/19.0/fs_attachment
VERSION
VERSION 1.1.1
CATEGORY
CATEGORYKnowledge Management
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, ACSONE SA/NV
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, ACSONE SA/NV
COMMITTERS
COMMITTERSStéphane Bidoul, Laurent Mignon (ACSONE), OCA-git-bot, Iván Todorovich, oca-ci
WEBSITE
WEBSITEhttps://github.com/OCA/storage
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 19:40:49
ODOO DEPENDENCIES
ODOO DEPENDENCIES OCA/server-env:
    - server_environment
OCA/storage:
    - fs_storage
odoo/odoo:
    - base
    - base_sparse_field
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES fsspec>=2025.3.0
python_slugify
fsspec>=2024.5.0
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
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 IDNameModelTypeStatus
fs_storage_form_view fs.storage.form (in fs_attachment) fs.storage field Inherits fs_storage.fs_storage_form_view
Models touched (4)

New fields (2)
  • fs_storage_code Char
    args: 'Storage Code'
  • store_fname Char
    args: 'Stored Filename'
Public methods (0)

No public methods.

New fields (9)
  • autovacuum_gc Boolean
    default=True help='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_url Char
    default=''
  • base_url_for_files Char
    compute='_compute_base_url_for_files' store=True
  • force_db_for_default_attachment_rules Text
    default=<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_url Boolean
    default=False help='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_path Boolean
    help="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_attachments Boolean
    default=False help='If checked, this storage will be used to store all the attachments '
  • use_filename_obfuscation Boolean
    help='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_url Boolean
    help="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'
Public methods (6)
  • create(self, vals_list)
    @api.model_create_multi
  • get_default_storage_code_for_attachments(self)
    @api.model
    Return 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) -> None
    Recompute 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_filename Char
    help='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_code Char
    related='fs_storage_id.code' store=True args: 'Filesystem Storage Code'
  • fs_storage_id Many2one → fs.storage
    compute='_compute_fs_storage_id' help='The storage where the file is stored.' ondelete='restrict' store=True args: 'fs.storage', 'Filesystem Storage'
  • fs_url Char
    compute='_compute_fs_url' help='The URL to access the file from the filesystem storage.' store=True args: 'Filesystem URL'
  • fs_url_path Char
    compute='_compute_fs_url_path' help='The path to access the file from the filesystem storage.' args: 'Filesystem URL Path'
  • internal_url Char
    compute='_compute_internal_url' help='The URL to access the file from the server.' args: 'Internal URL'
Public methods (5)
  • create(self, vals_list)
    @api.model_create_multi
    Storage 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.model
    Migrate 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.IOBase
    Return 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.

REPOSITORY
REPOSITORYOCA/storage
GIT
GIThttps://github.com/OCA/storage.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/storage/tree/18.0/fs_attachment
VERSION
VERSION 2.2.2
CATEGORY
CATEGORYKnowledge Management
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, ACSONE SA/NV
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, ACSONE SA/NV
COMMITTERS
COMMITTERSStéphane Bidoul, Laurent Mignon (ACSONE), Weblate, OCA-git-bot, Quentin Groulard, oca-ci, Vincent Hatakeyama, Pierre Halleux, Christopher Rogos, Laurent Mignonn (ACSONE)
WEBSITE
WEBSITEhttps://github.com/OCA/storage
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 19:30:15
ODOO DEPENDENCIES
ODOO DEPENDENCIES OCA/server-env:
    - server_environment
OCA/storage:
    - fs_storage
odoo/odoo:
    - base
    - base_sparse_field
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES fsspec>=2025.3.0
python_slugify
fsspec>=2024.5.0
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
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 IDNameModelTypeStatus
fs_storage_form_view fs.storage.form (in fs_attachment) fs.storage field Inherits fs_storage.fs_storage_form_view
Models touched (4)

New fields (2)
  • fs_storage_code Char
    args: 'Storage Code'
  • store_fname Char
    args: 'Stored Filename'
Public methods (0)

No public methods.

New fields (9)
  • autovacuum_gc Boolean
    default=True help='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_url Char
    default=''
  • base_url_for_files Char
    compute='_compute_base_url_for_files' store=True
  • force_db_for_default_attachment_rules Text
    default=<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_url Boolean
    default=False help='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_path Boolean
    help="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_attachments Boolean
    default=False help='If checked, this storage will be used to store all the attachments '
  • use_filename_obfuscation Boolean
    help='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_url Boolean
    help="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'
Public methods (6)
  • create(self, vals_list)
    @api.model_create_multi
  • get_default_storage_code_for_attachments(self)
    @api.model
    Return 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) -> None
    Recompute 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_filename Char
    help='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_code Char
    related='fs_storage_id.code' store=True args: 'Filesystem Storage Code'
  • fs_storage_id Many2one → fs.storage
    compute='_compute_fs_storage_id' help='The storage where the file is stored.' ondelete='restrict' store=True args: 'fs.storage', 'Filesystem Storage'
  • fs_url Char
    compute='_compute_fs_url' help='The URL to access the file from the filesystem storage.' store=True args: 'Filesystem URL'
  • fs_url_path Char
    compute='_compute_fs_url_path' help='The path to access the file from the filesystem storage.' args: 'Filesystem URL Path'
  • internal_url Char
    compute='_compute_internal_url' help='The URL to access the file from the server.' args: 'Internal URL'
Public methods (5)
  • create(self, vals_list)
    @api.model_create_multi
    Storage 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.model
    Migrate 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.IOBase
    Return 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.

REPOSITORY
REPOSITORYOCA/storage
GIT
GIThttps://github.com/OCA/storage.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/storage/tree/17.0/fs_attachment
VERSION
VERSION 1.6.2
CATEGORY
CATEGORYKnowledge Management
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, ACSONE SA/NV
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, ACSONE SA/NV
COMMITTERS
COMMITTERSStéphane Bidoul, Laurent Mignon (ACSONE), Weblate, OCA-git-bot, oca-ci, Vincent Hatakeyama, Houzéfa Abbasbhay, andrea, Fai, chien, Laurent Mignonn (ACSONE)
WEBSITE
WEBSITEhttps://github.com/OCA/storage
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 19:20:08
ODOO DEPENDENCIES
ODOO DEPENDENCIES OCA/server-env:
    - server_environment
OCA/storage:
    - fs_storage
odoo/odoo:
    - base
    - base_sparse_field
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES fsspec>=2025.3.0
python_slugify
fsspec>=2024.5.0
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
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 IDNameModelTypeStatus
fs_storage_form_view fs.storage.form (in fs_attachment) fs.storage field Inherits fs_storage.fs_storage_form_view
Models touched (6)

New fields (2)
  • fs_storage_code Char
    args: 'Storage Code'
  • store_fname Char
    args: 'Stored Filename'
Public methods (0)

No public methods.

New fields (13)
  • autovacuum_gc Boolean
    default=True help='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_url Char
    default=''
  • base_url_for_files Char
    compute='_compute_base_url_for_files' store=True
  • field_ids One2many → ir.model.fields
    compute='_compute_field_ids' help='List of fields such as attachments linked to one of these fields will be stored in this storage. NB: If the attachment is linked to a field that is in one FS storage, and the related model is in another FS storage, we will store it into the storage linked to the resource field.' inverse='_inverse_field_ids' args: 'ir.model.fields', 'storage_id'
  • field_xmlids Char
    help='List of fields xml ids such as attachments linked to one of these fields will be stored in this storage. NB: If the attachment is linked to a field that is in one FS storage, and the related model is in another FS storage, we will store it into the storage linked to the resource field.'
  • force_db_for_default_attachment_rules Text
    default=<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_url Boolean
    default=False help='Normally the directory_path is for internal usage. If this flag is enabled the path will be used to compute the public URL.'
  • model_ids One2many → ir.model
    compute='_compute_model_ids' help='List of models such as attachments linked to one of these models will be stored in this storage.' inverse='_inverse_model_ids' args: 'ir.model', 'storage_id'
  • model_xmlids Char
    help='List of models xml ids such as attachments linked to one of these models will be stored in this storage.'
  • optimizes_directory_path Boolean
    help="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_attachments Boolean
    default=False help='If checked, this storage will be used to store all the attachments '
  • use_filename_obfuscation Boolean
    help='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_url Boolean
    help="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'
Public methods (5)
  • create(self, vals_list)
    @api.model_create_multi
  • get_default_storage_code_for_attachments(self)
    @api.model@tools.ormcache_context(keys=['attachment_res_field', 'attachment_res_model'])
    Return 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.
  • recompute_urls(self) -> None
    Recompute 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_filename Char
    help='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_code Char
    related='fs_storage_id.code' store=True args: 'Filesystem Storage Code'
  • fs_storage_id Many2one → fs.storage
    compute='_compute_fs_storage_id' help='The storage where the file is stored.' ondelete='restrict' store=True args: 'fs.storage', 'Filesystem Storage'
  • fs_url Char
    compute='_compute_fs_url' help='The URL to access the file from the filesystem storage.' store=True args: 'Filesystem URL'
  • fs_url_path Char
    compute='_compute_fs_url_path' help='The path to access the file from the filesystem storage.' args: 'Filesystem URL Path'
  • internal_url Char
    compute='_compute_internal_url' help='The URL to access the file from the server.' args: 'Internal URL'
Public methods (5)
  • create(self, vals_list)
    @api.model_create_multi
    Storage 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.model
    Migrate 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.IOBase
    Return 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.

New fields (1)
  • storage_id Many2one → fs.storage
    help='If specified, all attachments linked to this model will be stored in the provided storage.' args: 'fs.storage'
Public methods (0)

No public methods.

New fields (1)
  • storage_id Many2one → fs.storage
    help='If specified, all attachments linked to this field will be stored in the provided storage.' args: 'fs.storage'
Public methods (0)

No public methods.

REPOSITORY
REPOSITORYOCA/storage
GIT
GIThttps://github.com/OCA/storage.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/storage/tree/16.0/fs_attachment
VERSION
VERSION 3.1.0
CATEGORY
CATEGORYKnowledge Management
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, ACSONE SA/NV
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, ACSONE SA/NV
COMMITTERS
COMMITTERSStéphane Bidoul, Laurent Mignon (ACSONE), Enric Tobella, Adrien Peiffer, Weblate, OCA-git-bot, Benjamin Willig, oca-ci, kobros-tech, andrea, len, Laurent Mignonn (ACSONE), Pablo Esteban
WEBSITE
WEBSITEhttps://github.com/OCA/storage
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 00:53:52
ODOO DEPENDENCIES
ODOO DEPENDENCIES OCA/storage:
    - fs_storage
odoo/odoo:
    - base
    - base_sparse_field
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES fsspec>=2025.3.0
python_slugify
fsspec>=2024.5.0
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION

Code Analysis

Views touched (1)
XML IDNameModelTypeStatus
fs_storage_form_view fs.storage.form (in fs_attachment) fs.storage field Inherits fs_storage.fs_storage_form_view
Models touched (6)

New fields (2)
  • fs_storage_code Char
    args: 'Storage Code'
  • store_fname Char
    args: 'Stored Filename'
Public methods (0)

No public methods.

New fields (13)
  • autovacuum_gc Boolean
    default=True help='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_url Char
    default=''
  • base_url_for_files Char
    compute='_compute_base_url_for_files' store=True
  • field_ids One2many → ir.model.fields
    compute='_compute_field_ids' help='List of fields such as attachments linked to one of these fields will be stored in this storage. NB: If the attachment is linked to a field that is in one FS storage, and the related model is in another FS storage, we will store it into the storage linked to the resource field.' inverse='_inverse_field_ids' args: 'ir.model.fields', 'storage_id'
  • field_xmlids Char
    help='List of fields xml ids such as attachments linked to one of these fields will be stored in this storage. NB: If the attachment is linked to a field that is in one FS storage, and the related model is in another FS storage, we will store it into the storage linked to the resource field.'
  • force_db_for_default_attachment_rules Text
    default=<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_url Boolean
    default=False help='Normally the directory_path is for internal usage. If this flag is enabled the path will be used to compute the public URL.'
  • model_ids One2many → ir.model
    compute='_compute_model_ids' help='List of models such as attachments linked to one of these models will be stored in this storage.' inverse='_inverse_model_ids' args: 'ir.model', 'storage_id'
  • model_xmlids Char
    help='List of models xml ids such as attachments linked to one of these models will be stored in this storage.'
  • optimizes_directory_path Boolean
    help="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_attachments Boolean
    default=False help='If checked, this storage will be used to store all the attachments '
  • use_filename_obfuscation Boolean
    help='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_url Boolean
    help="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'
Public methods (5)
  • create(self, vals_list)
    @api.model_create_multi
  • get_default_storage_code_for_attachments(self)
    @api.model@tools.ormcache_context(keys=['attachment_res_field', 'attachment_res_model'])
    Return 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.
  • recompute_urls(self) -> None
    Recompute 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_filename Char
    help='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_code Char
    related='fs_storage_id.code' store=True args: 'Filesystem Storage Code'
  • fs_storage_id Many2one → fs.storage
    compute='_compute_fs_storage_id' help='The storage where the file is stored.' ondelete='restrict' store=True args: 'fs.storage', 'Filesystem Storage'
  • fs_url Char
    compute='_compute_fs_url' help='The URL to access the file from the filesystem storage.' store=True args: 'Filesystem URL'
  • fs_url_path Char
    compute='_compute_fs_url_path' help='The path to access the file from the filesystem storage.' args: 'Filesystem URL Path'
  • internal_url Char
    compute='_compute_internal_url' help='The URL to access the file from the server.' args: 'Internal URL'
Public methods (5)
  • create(self, vals_list)
    @api.model_create_multi
    Storage 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.model
    Migrate 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.IOBase
    Return 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.

New fields (1)
  • storage_id Many2one → fs.storage
    help='If specified, all attachments linked to this model will be stored in the provided storage.' args: 'fs.storage'
Public methods (0)

No public methods.

New fields (1)
  • storage_id Many2one → fs.storage
    help='If specified, all attachments linked to this field will be stored in the provided storage.' args: 'fs.storage'
Public methods (0)

No public methods.