New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[IMP] account,hr_expense: allow ORM to prefetch attachments #117074
base: master
Are you sure you want to change the base?
[IMP] account,hr_expense: allow ORM to prefetch attachments #117074
Conversation
ba23285
to
aee66a2
Compare
8bd3a13
to
e4a09ce
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just saw that you abuse the ORM's field attribute automatic
for one accounting widget https://github.com/odoo/enterprise/pull/38991/files#diff-ee342c09ffb6b8de7f51c4a0ed66fee056e8975e7416d0f85ae0d2b6b1883dfdR204-R205. Please don't do that.
linked_attachment_ids = fields.One2many( | ||
comodel_name='ir.attachment', | ||
compute='_compute_linked_attachment_ids', | ||
groups="base.group_user", | ||
automatic=True, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please explain the presence of groups
and automatic
. What is their added value?
Please also justify polluting the field space of all models for optimizing one model.
It really looks like you are tweaking the framework for the accounting modules' needs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The groups
is basically because of this (and other similar access rights checks); it would raise errors in some tests because the field would be displayed otherwise, without enough rights to do so.
https://github.com/odoo/odoo/blob/e4a09ce62e165b8a33797702f89695debee15968/odoo/addons/base/models/ir_attachment.py#L445-L446
I will push a version without the automatic
, but as I understand it, it is there for the same reasons as for display_name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have pushed a version without those attributes set, here is the result:
https://runbot.odoo.com/runbot/batch/1062568/build/41373652
@william-andre adding a field on all models slows down the loading of the registry. That's why we removed the magic field Moreover, I just realized that your computed field has no dependency, and therefore has no cache consistency. That's not cool. I guess that's why you needed to add explicit cache invalidation in a test... So why don't you simply add a method |
See https://github.com/odoo/enterprise/pull/38991#discussion_r1209891985. |
a265d3e
to
e30aad0
Compare
6dd3aa6
to
8db2fe2
Compare
|
||
|
||
class LinkAttachmentMixin(models.AbstractModel): | ||
_name = 'link.attachment.mixin' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name is confusing: why "link" ? Simply follow conventions...
_name = 'link.attachment.mixin' | |
_name = 'ir.attachment.mixin' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ir.attachment.mixin
doesn't mean anything to me 😕
Maybe ir.attachment.link
? My thinking is that this mixin adds a link between ir.attachment
and the model inheriting the mixin
linked_attachment_ids = fields.One2many( | ||
comodel_name='ir.attachment', | ||
compute='_compute_linked_attachment_ids', | ||
inverse='_inverse_linked_attachment_ids', | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you use the following definition, invalidation is automatic:
linked_attachment_ids = fields.One2many( | |
comodel_name='ir.attachment', | |
compute='_compute_linked_attachment_ids', | |
inverse='_inverse_linked_attachment_ids', | |
) | |
linked_attachment_ids = fields.One2many( | |
'ir.attachment', 'res_id', | |
domain=lambda self: [('res_model', '=', self._name)], | |
readonly=True, string="Attachments", | |
) |
No need to compute, no need to add explicit invalidation in model ir.attachment
: all those things are supported by the framework. This solution's performance should be very close to yours.
I recommend to not modify attachments through this field. There's a widget for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A lot of tests won't pass with your suggestion.
I recommend to not modify attachments through this field. There's a widget for that.
Which widget?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't try to add magic on attachments. It is sufficiently complicated without adding yet another layer of issues (ACLs, res_model with chatter, ...) This seems like a good idea, but ends up like a bad idea to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is simplifying all the places where attachments are being manipulated manually, avoiding to do manual work where one could do mistakes...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I am not changing anything related to ACLs, everything is done using the ORM and public methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit like @rco-odoo , agree we could have a getter (or a field if you really want it), but don't allow updating / writing to those fields. I bet you are going to break a lot of cases in chatter / mailing, because res_model / res_id has a lot of complex use cases (linked to thread and not the message, because of document-related models, because you change the way ACLs will be managed on attachments, ...)
I don't see the added value of this PR compared to the fragility of current attachment model, that is worsened by this PR.
Cheers,
message_attachments = activity.linked_attachment_ids | ||
if message_attachments: | ||
activity_message.attachment_ids = message_attachments | ||
activity_message.linked_attachment_ids += message_attachments |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new code is less clear than the old code. It was explicit and clea. This one just pill up some "attachment_ids" but we don't understand what it is doing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I very strongly disagree, but it's your ownership I guess
This reads as simple as this, in only 4 lines
if there are attachments linked to the activity, link them to the activity message
Before it read like this, with code scattered with a span of 44 lines
get all the attachments linked to my record, group them by activity, if there are some for my activity, link them to the activity message
addons/mail/models/mail_message.py
Outdated
@@ -65,6 +65,7 @@ class Message(models.Model): | |||
information. | |||
""" | |||
_name = 'mail.message' | |||
_inherit = ['link.attachment.mixin'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I would prefer not having it on message. Attachments are generally linked to the thread, not the message.
linked_attachment_ids = fields.One2many( | ||
comodel_name='ir.attachment', | ||
compute='_compute_linked_attachment_ids', | ||
inverse='_inverse_linked_attachment_ids', | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't try to add magic on attachments. It is sufficiently complicated without adding yet another layer of issues (ACLs, res_model with chatter, ...) This seems like a good idea, but ends up like a bad idea to me.
995dfdd
to
b365545
Compare
65b0cd0
to
096b6ef
Compare
@rco-odoo @tde-banana-odoo These would be failing in general, not only for the new fields |
f9c6656
to
749e8a4
Compare
Currently, there is no invalidation whatsoever when writing on a one2many field that has a many2one_reference inverse. This can lead to weird behavior, such as * a record not being added to the field when updated through the ORM * the cache not being updated when changing the reference of the related field
… move line Before, we had to do one search per line instead of batching everything in one query per model. This leads to a linear time complexity to display the list view of Journal Entries.
This could have lead to incoherences with the security rules of the related attachment.
Instead of searching all the related attachments of the related model and then filtering them per record, use the ORM to already batch them per record. O(n^2) to O(n)
Instead of doing one search per expense to fetch the attchments, rely on the ORM to prefetch everything. Also create the attachments in batch when splitting an expense.
Everything works fine using a one2many field related to a many2one_reference out of the box. No need to complicate everything with a proxy field.
Instead of doing a manual search and grouping of the attachments, rely on the ORM to do the labour.
749e8a4
to
4f2af4b
Compare
This reverts commit 6679232. Performances shouldn't be fixed by duplicating all the attachments.
Hi @william-andre, |
Instead of having to search record by record thanks to the domain returned by
_get_attachment_domains
, we can use the ORM to fetch all the records at once.Benchmark
Speedup of 5x-6x (93ms to 16ms for 100 invoices)
Before
After
Setup
Here is my quick benchmark code:
Setup:
Test: