- 
                Notifications
    You must be signed in to change notification settings 
- Fork 79
          [IMP] util/records: enforce cascade removal for actions
          #81
        
          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
base: master
Are you sure you want to change the base?
Conversation
| I do not intend to leave these NOTES in the final diff, these are only for discussions in this PR | 
| You could move the notes from the code to the PR. Add them as comments here. | 
        
          
                src/util/records.py
              
                Outdated
          
        
      | _logger.log(NEARLYWARN, "Removing group %r", name) | ||
| return remove_group(cr, group_id=res_id) | ||
|  | ||
| if model in get_models_inheriting_actions_actions(cr): | 
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.
We could have a hard-coded list of standard action models. We don't need to take into account all possible models extending the actions base model. We don't that for views for example.
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.
We can just use for_each_inherit
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.
my point in leaving it as a SQL query is not only to encompass custom models but also reduce the maintainability overhead in case there is any change in standard models. what do you think?
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 SQL query version plus the query itself adds complexity, it's cleaner with for_each_inherit, IMO.
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.
OTOH, the list of models that are problematic (the ones that are using INHERIT at PostgreSQL level) is known. We can hard-code it.
Note that this list changed over time, so we need to list all of them.
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 ones that are using INHERIT at PostgreSQL level
AFAIU, the issue also encompasses models that would inherit ir.actions.actions without using psql's INHERIT mechanism*, since this concerns fields to ir.actions.actions (as a Polymorphism mechanism), and not its child classes.
*: I've just tested creating a new model inheriting from ir.actions.actions, similar to the standard ones, and this new model's table does not have Inherits: ir_actions, whereas the ones from standard do have.
So I understand that list of models returned by for_each_inherit should be equal to the ones to be hard-coded (or used in a hard-coded list) for a specific version.
That said, having in mind that for_each_inherit performs a SQL query itself, I believe that:
- hard-coding > for_each_inherit: more performatic
- for_each_inherit > hard-coding: easier to maintain
2019fa8    to
    c9c4053      
    Compare
  
            
          
                src/util/records.py
              
                Outdated
          
        
      | "%r should point to a model inheriting from 'ir.actions.actions', not a %r" % (xml_id, model) | ||
| ) | ||
|  | ||
| cr.execute("SELECT name, model FROM ir_model_fields WHERE relation = 'ir.actions.actions' AND on_delete ='cascade'") | 
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.
You should also search the one linking to the xmlid's model
| cr.execute("SELECT name, model FROM ir_model_fields WHERE relation = 'ir.actions.actions' AND on_delete ='cascade'") | |
| cr.execute("SELECT name, model FROM ir_model_fields WHERE relation IN %s AND on_delete ='cascade'", [tuple({model, "ir.actions.actions"})]) | 
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'm not sure I understood, maybe this is linked to the comment above?
I took the example of website.snippet.filter that has a M2O field to ir.actions.server. By examining it's table, we have:
Foreign-key constraints:
    "website_snippet_filter_action_server_id_fkey" FOREIGN KEY (action_server_id) REFERENCES ir_act_server(id) ON DELETE CASCADE
    "website_snippet_filter_create_uid_fkey" FOREIGN KEY (create_uid) REFERENCES res_users(id) ON DELETE SET NULL
    "website_snippet_filter_filter_id_fkey" FOREIGN KEY (filter_id) REFERENCES ir_filters(id) ON DELETE CASCADE
    "website_snippet_filter_website_id_fkey" FOREIGN KEY (website_id) REFERENCES website(id) ON DELETE CASCADE
    "website_snippet_filter_write_uid_fkey" FOREIGN KEY (write_uid) REFERENCES res_users(id) ON DELETE SET NULL
That is, the foreign-key to ir.actions.server is there (first line), so we can expect correct behaviour.
The issue is when we have a field to ir.actions.actions for polymorphism.
If indeed a confusion happened, please let me know so I can improve the commit message.
c9c4053    to
    e8ca86d      
    Compare
  
    0e6707b    to
    f293017      
    Compare
  
    3d68a7a    to
    9d3206b      
    Compare
  
    9d3206b    to
    8322013      
    Compare
  
    8322013    to
    af0ef5d      
    Compare
  
    | Can you add a test? | 
2b59b0a    to
    9fa89bd      
    Compare
  
    The implementation of the python inheritance mechanism between the base class `ir.actions.actions` and its child classes (eg. `ir.actions.act_window`) does not allow the creation of foreign keys when `ir.actions.actions` is a M2O field of another model when it is being referenced in favour of polymorphism; what leads to the not execution of some constraints, one of them being the `ondelete='cascade'` constraint, which is set in PSQL level. That said, when a `ir.actions.actions` record is deleted, if it is being referenced as a M2O field by another model (eg. `ir.filters`), records from this second model won't be affected, what leads to undesired behaviour: a MissingError in the UI, indicating that the action was deleted. Such behaviour of not creating foreign keys and thus constraints is specific to `ir.actions.actions`. This commit remedies this specific case, removing records with a M2O field to `ir.actions.actions` if `ondelete=cascade`, or setting this field to NULL if `ondelete=set null`, when the action referenced in such field is removed using `upgrade-util`. Co-authored-by: Christophe Simonis <KangOl@users.noreply.github.com>
9fa89bd    to
    7fc1c85      
    Compare
  
    | Hey I believe we can revert the following PR https://github.com/odoo/upgrade/pull/5869 before this  | 
| 
 No, this is still needed for actions not deleted via this new helper. | 
| @KangOl @aj-fuentes ping 🏓 | 
| Any news here? I've discovered another issue with filters on an upgrade project, though it's not related to upgrading technically -> install a module, like hr_holidays, save a filter, uninstall the module = the filter is now corrupt because of the missing model in registry: IMO, apart from this util helper, either: 2 - I fix this in a PR to  wdyt? @aj-fuentes @KangOl | 
| @erl-odoo The uninstall process of standard Odoo is full of corner cases where data is left in a wrong state, this is yet another example :) IMO yes you can try to fix the standard process to cleanup those filters in master odoo. During upgrades, removing what seems to be broken filters may be acceptable, but we usually try to avoid removing data (they may work in case the model re-appears after a reinstall). Generally speaking, if it was broken before the upgrade and doesn't fail the upgrade, we don't fix it. | 
| opw-3931090 | 

The implementation of the python inheritance mechanism between the base class
ir.actions.actionsand its child classes (eg.ir.actions.act_window) does not allow the creation of foreign keys whenir.actions.actionsis a M2O field of another model; what leads to the not execution of some constraints, one of them being theondelete='cascade'constraint, which is set in PSQL level.That said, when a
ir.actions.actionsrecord is deleted, if it is being referenced as a M2O field by another model (eg.ir.filters), records from this second model won't be affected, what leads to undesired behaviour: a MissingError in the UI, indicating that the action was deleted.Such behaviour of not creating foreign keys and thus constraints is specific to
ir.actions.actions.This commit remedies this specific case, removing records with a M2O field to
ir.actions.actionswithondelete=cascadewhen the action referenced in such field is removed usingupgrade-util.