[FIX] website: delete cow view on module update

Before this commit, when a view was removed during a module update (eg: the
record was deleted from the file), its COW views would not be deleted.

This could lead to unwanted behaviors including tracebacks(1).

This commit is somehow related/an extension of b5fe23055d that handle COW view
write during module update and 2e32cc5aa3 that remove COW views during module
uninstall.

task-1931683

Note: Some existing tests had to be run post install

(1) A view doing a t-call is COW'd, then that view and its t-called view are
    removed. If the cow view is not removed, the t-call will crash (see tests
    in this commit for detailed case).

closes odoo/odoo#31295

Signed-off-by: Jérémy Kersten (jke) <jke@openerp.com>
This commit is contained in:
Romain Derie
2019-03-05 11:04:52 +00:00
parent dfb5e3791f
commit 5a81d88837
6 changed files with 88 additions and 2 deletions
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- RECORDS FOR RESET VIEWS TESTS -->
<record id="test_view" model="ir.ui.view">
<field name="name">Test View</field>
<field name="type">qweb</field>
@@ -50,5 +52,11 @@
<p>placeholder</p>
</xpath>
</template>
<!-- RECORDS FOR MODULE OPERATION TESTS -->
<template id="update_module_base_view">
<div>I am a base view</div>
</template>
</data>
</odoo>
+1
View File
@@ -2,3 +2,4 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import test_reset_views
from . import test_views_during_module_operation
@@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.tests import TransactionCase, tagged
@tagged('-at_install', 'post_install')
class TestViewsDuringModuleOperation(TransactionCase):
def test_01_cow_views_unlink_on_module_update(self):
""" Ensure COW views are correctly removed during module update.
Not removing the view could lead to traceback:
- Having a view A
- Having a view B that inherits from a view C
- View B t-call view A
- COW view B
- Delete view A and B from module datas and update it
- Rendering view C will crash since it will render child view B that
t-call unexisting view A
"""
View = self.env['ir.ui.view']
Imd = self.env['ir.model.data']
update_module_base_view = self.env.ref('test_website.update_module_base_view')
update_module_view_to_be_t_called = View.create({
'name': 'View to be t-called',
'type': 'qweb',
'arch': '<div>I will be t-called</div>',
'key': 'test_website.update_module_view_to_be_t_called',
})
update_module_child_view = View.create({
'name': 'Child View',
'mode': 'extension',
'inherit_id': update_module_base_view.id,
'arch': '''
<div position="inside">
<t t-call="test_website.update_module_view_to_be_t_called"/>
</div>
''',
'key': 'test_website.update_module_child_view',
})
# Create IMD so when updating the module the views will be removed (not found in file)
Imd.create({
'module': 'test_website',
'name': 'update_module_view_to_be_t_called',
'model': 'ir.ui.view',
'res_id': update_module_view_to_be_t_called.id,
})
Imd.create({
'module': 'test_website',
'name': 'update_module_child_view',
'model': 'ir.ui.view',
'res_id': update_module_child_view.id,
})
# Trigger COW on child view
update_module_child_view.with_context(website_id=1).write({'name': 'Child View (W1)'})
# Ensure views are correctly setup
self.assertEquals(View.search_count([('type', '=', 'qweb'), ('key', '=', update_module_child_view.key)]), 2)
self.assertTrue(self.env.ref(update_module_view_to_be_t_called.key))
self.assertTrue(self.env.ref(update_module_base_view.key))
# Uninstall module
test_website_module = self.env['ir.module.module'].search([('name', '=', 'test_website')])
test_website_module.button_immediate_upgrade()
# Ensure generic views got removed
self.assertFalse(self.env.ref('test_website.update_module_view_to_be_t_called', raise_if_not_found=False))
# Ensure specific COW views got removed
self.assertEquals(View.search_count([('type', '=', 'qweb'), ('key', '=', 'test_website.update_module_child_view')]), 0)
+3
View File
@@ -158,6 +158,9 @@ class View(models.Model):
# care of creating pages and menus.
view.with_context(website_id=website.id).write({'name': view.name})
if self.pool._init:
self += self._get_specific_views()
result = super(View, self).unlink()
self.clear_caches()
return result
+2 -1
View File
@@ -1,7 +1,8 @@
# coding: utf-8
from odoo.tests import common, HttpCase
from odoo.tests import common, HttpCase, tagged
@tagged('-at_install', 'post_install')
class TestPage(common.TransactionCase):
def setUp(self):
super(TestPage, self).setUp()
+2 -1
View File
@@ -10,7 +10,7 @@ import unittest
from lxml import etree as ET, html
from lxml.html import builder as h
from odoo.tests import common, HttpCase
from odoo.tests import common, HttpCase, tagged
def attrs(**kwargs):
@@ -264,6 +264,7 @@ class TestViewSaving(common.TransactionCase):
)
@tagged('-at_install', 'post_install')
class TestCowViewSaving(common.TransactionCase):
def setUp(self):
super(TestCowViewSaving, self).setUp()