Skip to content

Commit 70cb033

Browse files
committed
[19.0][MIG] fs_attachment Migration 19.0
1 parent ab67d11 commit 70cb033

File tree

9 files changed

+30
-37
lines changed

9 files changed

+30
-37
lines changed

.pre-commit-config.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
exclude: |
22
(?x)
33
# NOT INSTALLABLE ADDONS
4-
^fs_attachment/|
54
^fs_attachment_s3/|
65
^fs_file/|
76
^fs_folder/|

fs_attachment/__manifest__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
{
66
"name": "Base Attachment Object Store",
77
"summary": "Store attachments on external object store",
8-
"version": "18.0.2.1.0",
8+
"version": "19.0.1.0.0",
99
"author": "Camptocamp, ACSONE SA/NV, Odoo Community Association (OCA)",
1010
"license": "AGPL-3",
1111
"development_status": "Beta",
@@ -17,7 +17,7 @@
1717
"views/fs_storage.xml",
1818
],
1919
"external_dependencies": {"python": ["python_slugify", "fsspec>=2025.3.0"]},
20-
"installable": False,
20+
"installable": True,
2121
"auto_install": False,
2222
"maintainers": ["lmignon"],
2323
"pre_init_hook": "pre_init_hook",

fs_attachment/models/fs_file_gc.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,10 @@ class FsFileGC(models.Model):
1717
store_fname = fields.Char("Stored Filename")
1818
fs_storage_code = fields.Char("Storage Code")
1919

20-
_sql_constraints = [
21-
(
22-
"store_fname_uniq",
23-
"unique (store_fname)",
24-
"The stored filename must be unique!",
25-
),
26-
]
20+
_store_fname_uniq = models.Constraint(
21+
"unique (store_fname)",
22+
"The stored filename must be unique!",
23+
)
2724

2825
def _is_test_mode(self) -> bool:
2926
"""Return True if we are running the tests, so we do not mark files for
@@ -101,7 +98,7 @@ def _gc_files(self) -> None:
10198
# the LOCK statement will wait until those concurrent transactions end.
10299
# But this transaction will not see the new attachements if it has done
103100
# other requests before the LOCK (like the method _storage() above).
104-
cr = self._cr
101+
cr = self.env.cr
105102
cr.commit() # pylint: disable=invalid-commit
106103

107104
# prevent all concurrent updates on ir_attachment and fs_file_gc
@@ -125,7 +122,7 @@ def _gc_files_unsafe(self) -> None:
125122
if not codes:
126123
return
127124
# we process by batch of storage codes.
128-
self._cr.execute(
125+
self.env.cr.execute(
129126
"""
130127
SELECT
131128
fs_storage_code,
@@ -145,7 +142,7 @@ def _gc_files_unsafe(self) -> None:
145142
""",
146143
(tuple(codes),),
147144
)
148-
for code, store_fnames in self._cr.fetchall():
145+
for code, store_fnames in self.env.cr.fetchall():
149146
self.env["fs.storage"].get_by_code(code)
150147
fs = self.env["fs.storage"].get_fs_by_code(code)
151148
for store_fname in store_fnames:
@@ -156,7 +153,7 @@ def _gc_files_unsafe(self) -> None:
156153
_logger.debug("Failed to remove file %s", store_fname)
157154

158155
# delete the records from the table fs_file_gc
159-
self._cr.execute(
156+
self.env.cr.execute(
160157
"""
161158
DELETE FROM
162159
fs_file_gc

fs_attachment/models/fs_storage.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def _check_use_as_default_for_attachments(self):
8585
defaults = self.search([]).filtered("use_as_default_for_attachments")
8686
if len(defaults) > 1:
8787
raise ValidationError(
88-
_("Only one storage can be used as default for attachments")
88+
self.env._("Only one storage can be used as default for attachments")
8989
)
9090

9191
@property
@@ -132,7 +132,7 @@ def write(self, vals):
132132
if not vals["use_as_default_for_attachments"]:
133133
vals["force_db_for_default_attachment_rules"] = None
134134
res = super().write(vals)
135-
self._create_write_check_constraints(vals)
135+
self.env.create_write_check_constraints(vals)
136136
return res
137137

138138
def _create_write_check_constraints(self, vals):
@@ -165,7 +165,7 @@ def _check_force_db_for_default_attachment_rules(self):
165165
continue
166166
if not rec.use_as_default_for_attachments:
167167
raise ValidationError(
168-
_(
168+
self.env._(
169169
"The force_db_for_default_attachment_rules can only be set "
170170
"if the storage is used as default for attachments."
171171
)
@@ -174,7 +174,7 @@ def _check_force_db_for_default_attachment_rules(self):
174174
const_eval(rec.force_db_for_default_attachment_rules)
175175
except (SyntaxError, TypeError, ValueError) as e:
176176
raise ValidationError(
177-
_(
177+
self.env._(
178178
"The force_db_for_default_attachment_rules is not a valid "
179179
"python dict."
180180
)

fs_attachment/models/ir_attachment.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818
from slugify import slugify # pylint: disable=missing-manifest-dependency
1919

2020
import odoo
21-
from odoo import _, api, fields, models
21+
from odoo import api, fields, models
2222
from odoo.exceptions import AccessError, UserError
23-
from odoo.osv.expression import AND, OR, normalize_domain
23+
from odoo.fields import Domain
2424

2525
from .strtobool import strtobool
2626

@@ -169,9 +169,9 @@ def _store_in_db_instead_of_object_storage_domain(self):
169169
for mimetype_key, limit in storage_config.items():
170170
part = [("mimetype", "=like", f"{mimetype_key}%")]
171171
if limit:
172-
part = AND([part, [("file_size", "<=", limit)]])
172+
part = Domain.AND([part, [("file_size", "<=", limit)]])
173173
# OR simplifies to [(1, '=', 1)] if a domain being OR'ed is empty
174-
domain = OR([domain, part]) if domain else part
174+
domain = Domain.OR([domain, part]) if domain else part
175175
return domain
176176

177177
def _store_in_db_instead_of_object_storage(self, data, mimetype):
@@ -306,7 +306,7 @@ def write(self, vals):
306306
vals["mimetype"] = mimetypes[0]
307307
else:
308308
raise UserError(
309-
_(
309+
self.env._(
310310
"You can't write on multiple attachments with different "
311311
"mimetypes at the same time."
312312
)
@@ -723,7 +723,9 @@ def _move_attachment_to_store(self):
723723
@api.model
724724
def force_storage(self):
725725
if not self.env["res.users"].browse(self.env.uid)._is_admin():
726-
raise AccessError(_("Only administrators can execute this action."))
726+
raise AccessError(
727+
self.env._("Only administrators can execute this action.")
728+
)
727729
location = self.env.context.get("storage_location") or self._storage()
728730
if location not in self._get_storage_codes():
729731
return super().force_storage()
@@ -762,9 +764,9 @@ def force_storage_to_db_for_special_fields(
762764
)
763765
return
764766

765-
domain = AND(
767+
domain = Domain.AND(
766768
(
767-
normalize_domain(
769+
Domain.normalize_domain(
768770
[
769771
("store_fname", "=like", f"{storage}://%"),
770772
# for res_field, see comment in
@@ -774,7 +776,9 @@ def force_storage_to_db_for_special_fields(
774776
("res_field", "!=", False),
775777
]
776778
),
777-
normalize_domain(self._store_in_db_instead_of_object_storage_domain()),
779+
Domain.normalize_domain(
780+
self._store_in_db_instead_of_object_storage_domain()
781+
),
778782
)
779783
)
780784

fs_attachment/tests/test_fs_attachment.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ def test_create_attachments_basic_user(self):
396396
group_user = self.env.ref("base.group_user")
397397
group_partner_manager = self.env.ref("base.group_partner_manager")
398398
demo_user.write(
399-
{"groups_id": [(6, 0, [group_user.id, group_partner_manager.id])]}
399+
{"groups_ids": [(6, 0, [group_user.id, group_partner_manager.id])]}
400400
)
401401
# Create basic attachment
402402
self.ir_attachment_model.with_user(demo_user).create(

fs_attachment/tests/test_fs_attachment_file_like_adapter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def prepareClass(cls):
1616
cls.new_content = b"This is a new test attachment"
1717

1818
def prepare(self):
19-
self.attachment = self._create_attachment()
19+
self.attachment = self.env.create_attachment()
2020

2121
def open(self, attachment=None, mode="rb", new_version=False, **kwargs):
2222
return AttachmentFileLikeAdapter(

fs_attachment/tests/test_stream.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ def test_serving_field_image(self):
170170
demo_partner = self.env.ref("base.partner_demo")
171171
demo_partner.with_context(
172172
storage_location=self.temp_backend.code,
173-
).write({"image_128": base64.encodebytes(self._create_image(128, 128))})
173+
).write({"image_128": base64.encodebytes(self.env.create_image(128, 128))})
174174
url = f"/web/image/{demo_partner._name}/{demo_partner.id}/image_128"
175175
res = self.assertDownload(
176176
url,

requirements.txt

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
11
# generated from manifests external_dependencies
2-
boto3
3-
fsspec>=2024.5.0
4-
fsspec>=2025.0.0
52
fsspec>=2025.3.0
6-
fsspec[s3]
7-
msgraphfs
8-
paramiko
9-
pyftpdlib
103
python_slugify

0 commit comments

Comments
 (0)