Skip to content

[FIX] util/records:Mark records noupdate #287

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

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

sagu-odoo
Copy link
Contributor

@sagu-odoo sagu-odoo commented Jun 19, 2025

If particular xml don't have noupdate marked but parent data node have noupdate in that case from update_records_from_xml creating record as noupdate false which is causing issue in future upgrade because it going for delete. For preventing such case adding noupdate flag to record and inherit created record acccoring to this commit by that //data node or that xml node itself.

use case:
client db don't have the documents.document_internal_folder xmlid which is created when migrated to 18.0 from this script with noupdate false and because data node have noupdate true but particular xml don't have noupdate that is why it created as noupdate false and other inherit records also with noupdate and after migrating from onwards version related to that mail.alias records xml creating according to this

and later on version in 18.3 if related inherit model is changed but still some how use case of those records for preventing in that case.

before upgrade

timer_test_document_test=# select * from ir_model_data where name like '%document_internal_folder%'
;
 id | create_uid | create_date | write_date | write_uid | res_id | noupdate | name | module | model
----+------------+-------------+------------+-----------+--------+----------+------+--------+-------
(0 rows)

before fix

timer_test_document_test=# select * from ir_model_data where name like '%document_internal_folder%'
;
  id   | create_uid |        create_date         |         write_date         | write_uid | res_id | noupdate |                name                 |  module   |       model
-------+------------+----------------------------+----------------------------+-----------+--------+----------+-------------------------------------+-----------+--------------------
 39576 |            | 2025-06-19 13:38:37.987267 | 2025-06-19 13:38:37.987267 |           |     20 | f        | document_internal_folder_mail_alias | documents | mail.alias
 39577 |            | 2025-06-19 13:38:37.987267 | 2025-06-19 13:38:37.987267 |           |     20 | f        | document_internal_folder            | documents | documents.document

after fix

timer_test_document_test=# select * from ir_model_data where name like '%document_internal_folder%'
;
  id   | create_uid |        create_date         |         write_date         | write_uid | res_id | noupdate |                name                 |  module   |       model
-------+------------+----------------------------+----------------------------+-----------+--------+----------+-------------------------------------+-----------+--------------------
 39576 |            | 2025-06-19 13:41:40.343454 | 2025-06-19 13:41:40.343454 |           |     20 | t        | document_internal_folder_mail_alias | documents | mail.alias
 39577 |            | 2025-06-19 13:41:40.343454 | 2025-06-19 13:41:40.343454 |           |     20 | t        | document_internal_folder            | documents | documents.document

Traceback (most recent call last):
  File "/home/odoo/src/odoo/saas-18.3/odoo/service/server.py", line 1396, in preload_registries
    registry = Registry.new(dbname, update_module=update_module, install_modules=config['init'], upgrade_modules=config['update'])
  File "<decorator-gen-6>", line 2, in new
  File "/home/odoo/src/odoo/saas-18.3/odoo/tools/func.py", line 83, in locked
    return func(inst, *args, **kwargs)
  File "/home/odoo/src/odoo/saas-18.3/odoo/orm/registry.py", line 167, in new
    load_modules(
  File "/home/odoo/src/odoo/saas-18.3/odoo/modules/loading.py", line 509, in load_modules
    env['ir.model.data']._process_end(registry.updated_modules)
  File "/tmp/tmp2ipekce9/migrations/base/0.0.0/pre-models-no-model-data-delete.py", line 108, in _process_end
    return super(IrModelData, self)._process_end(modules)
  File "/home/odoo/src/odoo/saas-18.3/odoo/addons/base/models/ir_model.py", line 2589, in _process_end
    self._process_end_unlink_record(record)
  File "/home/odoo/src/odoo/saas-18.3/addons/website/models/ir_model_data.py", line 35, in _process_end_unlink_record
    return super()._process_end_unlink_record(record)
  File "/home/odoo/src/odoo/saas-18.3/odoo/addons/base/models/ir_model.py", line 2518, in _process_end_unlink_record
    record.unlink()
  File "/home/odoo/src/odoo/saas-18.3/odoo/orm/models.py", line 3913, in unlink
    cr.execute(SQL(
  File "/home/odoo/src/odoo/saas-18.3/odoo/sql_db.py", line 422, in execute
    self._obj.execute(query, params)
psycopg2.errors.ForeignKeyViolation: update or delete on table "mail_alias" violates foreign key constraint "documents_document_alias_id_fkey" on table "documents_document"
DETAIL:  Key (id)=(40) is still referenced from table "documents_document".

upg-2987024
opw-4874805

@robodoo
Copy link
Contributor

robodoo commented Jun 19, 2025

Pull request status dashboard

@aj-fuentes
Copy link
Contributor

I'm not sure we want this. We could call force_noupdate in the caller script. Or perhaps add a noupdate=(True/False/None) (None -> keep whatever is already there) to update_record_from_xml.

@KangOl wdyt?

@KangOl
Copy link
Contributor

KangOl commented Jun 19, 2025

upgradeci retry with always only documents

@KangOl
Copy link
Contributor

KangOl commented Jun 19, 2025

Indeed, it should not be automatically determined.
A noupdate:bool | None = None attribute can be added to the function.

@sagu-odoo sagu-odoo force-pushed the master-mark_noupdate_flag-sagu branch 2 times, most recently from eca8e4e to a9a0894 Compare June 20, 2025 04:53
@sagu-odoo
Copy link
Contributor Author

Hello @aj-fuentes , @KangOl Thanks for your quick review. I have update patch so it will work only whenever new record will be created. your insight will be valuable and it will prevent blocking for future case also That's the main purpose of this fix

@sagu-odoo sagu-odoo marked this pull request as ready for review June 20, 2025 04:57
@sagu-odoo sagu-odoo force-pushed the master-mark_noupdate_flag-sagu branch 4 times, most recently from 64cc32c to 938f3a0 Compare June 20, 2025 06:38
Comment on lines 1162 to 1168
data_node = doc.find(".//data")
if (data_node is not None and eval(data_node.attrib.get("noupdate", "0"))) or (
eval(node.attrib.get("noupdate", "0"))
):
force_noupdate_to_record = True

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is to NOT do this.

Suggested change
data_node = doc.find(".//data")
if (data_node is not None and eval(data_node.attrib.get("noupdate", "0"))) or (
eval(node.attrib.get("noupdate", "0"))
):
force_noupdate_to_record = True

Copy link
Contributor Author

@sagu-odoo sagu-odoo Jun 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but if record is noupdate False which is created in that case ?. :(

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but if record is noupdate False which is created in that case ?. :(

I don't understand what you meant.

@sagu-odoo sagu-odoo force-pushed the master-mark_noupdate_flag-sagu branch from 938f3a0 to fbb5ae7 Compare June 20, 2025 07:10
@@ -1210,6 +1223,17 @@ def add_ref(ref):
if reset_write_metadata and write_data:
cr.execute("UPDATE {} SET write_uid=%s, write_date=%s WHERE id=%s".format(table), write_data)

# force noupdate newly created record and inherit records
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you meant here. But I think what you could do is to put the noupdate flag in new_root such that any new record created from it gets the noupdate flag.

Doing this post load may miss some newly created records (and children).

Comment on lines 1162 to 1168
data_node = doc.find(".//data")
if (data_node is not None and eval(data_node.attrib.get("noupdate", "0"))) or (
eval(node.attrib.get("noupdate", "0"))
):
force_noupdate_to_record = True

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but if record is noupdate False which is created in that case ?. :(

I don't understand what you meant.

@KangOl
Copy link
Contributor

KangOl commented Jun 20, 2025

OK, I think I finally understand the initial problem.
We don't need this new noupdate=None argument.

The purpose of this function is to bypass the noupdate=1 flag in the XML declarations. So we need to force the record to be in noupdate when it is newly created.

diff --git src/util/records.py src/util/records.py
index 0ea524d..a7360aa 100644
--- src/util/records.py
+++ src/util/records.py
@@ -1108,7 +1108,8 @@ def __update_record_from_xml(
         return
     else:
         # The xmlid doesn't already exists, nothing to reset
-        reset_write_metadata = noupdate = reset_translations = False
+        reset_write_metadata = reset_translations = False
+        noupdate = True
         fields = None
 
     write_data = None

Then we have a second issue with the xmlids of the parent records in case of _inherits.
For this, I propose to change the force_noupdate function to directly handle that case.

@sagu-odoo sagu-odoo force-pushed the master-mark_noupdate_flag-sagu branch from fbb5ae7 to 0b16db2 Compare June 20, 2025 08:36
@sagu-odoo
Copy link
Contributor Author

sagu-odoo commented Jun 20, 2025

Indeed @KangOl and @aj-fuentes , I was thinkin for the case where the record with noupdate False come for update_record_from_xml but that will never happen my bad and made fix according to that, anyway I have updated the patch and tested also.

Thanks

@sagu-odoo sagu-odoo force-pushed the master-mark_noupdate_flag-sagu branch from 0b16db2 to f96207d Compare June 20, 2025 08:42
@KangOl KangOl force-pushed the master-mark_noupdate_flag-sagu branch from f96207d to 13eaf26 Compare June 20, 2025 09:45
@KangOl
Copy link
Contributor

KangOl commented Jun 20, 2025

@sagu-odoo can you test with the last version?

@sagu-odoo
Copy link
Contributor Author

sagu-odoo commented Jun 20, 2025

@KangOl , here it's -> inherits instead of _inherit

so neeed to get inherits and mark them as noupdate

(Pdb) util.env(cr)['documents.document']._inherit
['mail.thread.cc', 'mail.activity.mixin', 'mail.alias.mixin']
(Pdb) util.env(cr)['documents.document']._inherits
{'mail.alias': 'alias_id'}
(Pdb) 

@KangOl
Copy link
Contributor

KangOl commented Jun 20, 2025

Yes, this is filtered by the if inh.via: condition.

@sagu-odoo
Copy link
Contributor Author

sagu-odoo commented Jun 20, 2025

for the case I am testing for that it's not working. due to this,mail.alias is not directy inherited but it inherited from mail.alias.mixin and mail.alias.mixin is inherited in document.documents so it related to this mail.alias created record didn't marked as noupdate may be

during debugging that i found that mail_alias didn't yield here

> /home/odoo/odoo18/upgrade-util/src/util/inherit.py(106)direct_inherit_parents()
-> for inh in inhs:
(Pdb) n
> /home/odoo/odoo18/upgrade-util/src/util/inherit.py(107)direct_inherit_parents()
-> if inh.model == model and cmp_(inh):
(Pdb) parent
'mail.alias'
(Pdb) cmp_(inh)
True
(Pdb) inh.model == model
False
(Pdb) model
'documents.document'
(Pdb) inh.model
'mail.alias.mixin'

@KangOl
Copy link
Contributor

KangOl commented Jun 20, 2025

Ho! The usage of direct_inherit_parents is not enough.
Thanks for pointing it.

Don't yield grand_parent multiple times.
@KangOl KangOl force-pushed the master-mark_noupdate_flag-sagu branch from 13eaf26 to 0d5f106 Compare June 20, 2025 13:20
@KangOl KangOl requested a review from aj-fuentes June 20, 2025 13:21
Function that yield inherit*s* parent and the delegate m2o field.
KangOl added 4 commits June 20, 2025 18:48
Use the new `inherits_parents` function to get the inherits models.
As the purpose of this function is to bypass the `noupdate=1` attribute
in the XML declarations, when we create the record, we should flag it as
noupdate.
For models with `_inherits`, an xmlid is created for the parent record.
We also need to update those xmlids.

As example, when called on a `product.product` record, we now also set
the flag on the xmlid of the `product.template` record.
Set the record in noupdate=False using the `force_noupdate` function,
taking adventage of its new behavior of also updating the parent records.
@KangOl KangOl force-pushed the master-mark_noupdate_flag-sagu branch from 0d5f106 to 75b0d78 Compare June 20, 2025 16:48
@sagu-odoo
Copy link
Contributor Author

Hello @KangOl , I have tested with this new patch but here due to _cached decorator it not working because if model inherit is changed during upgrade those are not updated because it already in _result and changes regarding noupdate didn't reflected accordingly to that _inherits record because it never got that model in direct_inherits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants