[IMP] website: remove cache from website.page
The cache placed on the pages is no longer useful thanks to the use of the new directive t-cache. closes odoo/odoo#88276 Related: odoo/enterprise#27582 Related: odoo/documentation#2056 Related: odoo/upgrade#3451 Signed-off-by: Vincent Schippefilt (vsc) <vsc@odoo.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
<odoo>
|
||||
<template id="frontend_layout" name="Main Frontend Layout" inherit_id="web.frontend_layout">
|
||||
<xpath expr="//div[@id='wrapwrap']" position="attributes">
|
||||
<attribute name="t-attf-class" add="#{request.env['res.lang']._lang_get(request.env.lang).direction == 'rtl' and 'o_rtl' or ''}" separator=" "/>
|
||||
<attribute name="t-attf-class" add="#{request.env['res.lang']._lang_get_direction(request.env.lang) == 'rtl' and 'o_rtl' or ''}" separator=" "/>
|
||||
<attribute name="t-attf-class" add="#{'o_portal' if is_portal else ''}" separator=" "/>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='wrapwrap']/header/img" position="replace">
|
||||
|
||||
@@ -420,7 +420,7 @@ class TestOnlineEventPerformance(EventPerformanceCase, UtilPerf):
|
||||
])
|
||||
|
||||
def _test_url_open(self, url):
|
||||
url += ('?' not in url and '?' or '') + '&nocache'
|
||||
url += ('?' not in url and '?' or '') + '&debug=disable-t-cache'
|
||||
return self.url_open(url)
|
||||
|
||||
@warmup
|
||||
|
||||
@@ -28,7 +28,7 @@ You can use any truthy/falsy value from `str2bool` (eg: 'on', 'f'..)
|
||||
Multiple debug modes can be activated simultaneously, separated with a
|
||||
comma (eg: 'tests, assets').
|
||||
"""
|
||||
ALLOWED_DEBUG_MODES = ['', '1', 'assets', 'tests']
|
||||
ALLOWED_DEBUG_MODES = ['', '1', 'assets', 'tests', 'disable-t-cache']
|
||||
|
||||
|
||||
class Http(models.AbstractModel):
|
||||
|
||||
@@ -523,7 +523,6 @@
|
||||
<field name="url">/contactus</field>
|
||||
<field name="is_published">True</field>
|
||||
<field name="view_id" ref="contactus"/>
|
||||
<field name="cache_time">0</field>
|
||||
<field name="track">True</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -267,48 +267,12 @@ class Http(models.AbstractModel):
|
||||
path += '?' + request.httprequest.query_string.decode('utf-8')
|
||||
return request.redirect(path, code=301)
|
||||
|
||||
if page:
|
||||
# prefetch all menus (it will prefetch website.page too)
|
||||
menu_pages_ids = request.website._get_menu_page_ids()
|
||||
page.browse([page.id] + menu_pages_ids).mapped('view_id.name')
|
||||
request.website.menu_id
|
||||
|
||||
if page and (request.website.is_publisher() or page.is_visible):
|
||||
need_to_cache = False
|
||||
cache_key = page._get_cache_key(request)
|
||||
if (
|
||||
page.cache_time # cache > 0
|
||||
and request.httprequest.method == "GET"
|
||||
and request.env.user._is_public() # only cache for unlogged user
|
||||
and 'nocache' not in request.httprequest.args # allow bypass cache / debug
|
||||
and not request.session.debug
|
||||
and len(cache_key) and cache_key[-1] is not None # nocache via expr
|
||||
):
|
||||
need_to_cache = True
|
||||
try:
|
||||
r = page._get_cache_response(cache_key)
|
||||
if r['time'] + page.cache_time > time.time():
|
||||
response = odoo.http.Response(r['content'], mimetype=r['contenttype'])
|
||||
response._cached_template = r['template']
|
||||
response._cached_page = page
|
||||
return response
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
_, ext = os.path.splitext(req_page)
|
||||
response = request.render(page.view_id.id, {
|
||||
'deletable': True,
|
||||
'main_object': page,
|
||||
}, mimetype=_guess_mimetype(ext))
|
||||
|
||||
if need_to_cache and response.status_code == 200:
|
||||
r = response.render()
|
||||
page._set_cache_response(cache_key, {
|
||||
'content': r,
|
||||
'contenttype': response.headers['Content-Type'],
|
||||
'time': time.time(),
|
||||
'template': getattr(response, 'qcontext', {}).get('response_template')
|
||||
})
|
||||
return response
|
||||
return False
|
||||
|
||||
|
||||
@@ -105,14 +105,6 @@ class View(models.Model):
|
||||
|
||||
pages = view.page_ids
|
||||
|
||||
# Disable cache of page if we guess some dynamic content (form with csrf, ...)
|
||||
if vals.get('arch'):
|
||||
to_invalidate = pages.filtered(
|
||||
lambda p: p.cache_time and not p._can_be_cached(vals['arch'])
|
||||
)
|
||||
to_invalidate and _logger.info('Disable cache for page %s', to_invalidate)
|
||||
to_invalidate.cache_time = 0
|
||||
|
||||
# No need of COW if the view is already specific
|
||||
if view.website_id:
|
||||
super(View, view).write(vals)
|
||||
|
||||
@@ -176,11 +176,6 @@ class Website(models.Model):
|
||||
def _get_menu_ids(self):
|
||||
return self.env['website.menu'].search([('website_id', '=', self.id)]).ids
|
||||
|
||||
# self.env.uid for ir.rule groups on menu
|
||||
@tools.ormcache('self.env.uid', 'self.id')
|
||||
def _get_menu_page_ids(self):
|
||||
return self.env['website.menu'].search([('website_id', '=', self.id)]).page_id.ids
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
|
||||
@@ -98,10 +98,8 @@ class Menu(models.Model):
|
||||
return menus
|
||||
|
||||
def write(self, values):
|
||||
res = super().write(values)
|
||||
if 'website_id' in values or 'sequence' in values or 'page_id' in values:
|
||||
self.clear_caches()
|
||||
return res
|
||||
self.clear_caches()
|
||||
return super().write(values)
|
||||
|
||||
def unlink(self):
|
||||
self.clear_caches()
|
||||
|
||||
@@ -31,9 +31,6 @@ class Page(models.Model):
|
||||
is_homepage = fields.Boolean(compute='_compute_homepage', inverse='_set_homepage', string='Homepage')
|
||||
is_visible = fields.Boolean(compute='_compute_visible', string='Is Visible')
|
||||
|
||||
cache_time = fields.Integer(default=3600, help='Time to cache the page. (0 = no cache)')
|
||||
cache_key_expr = fields.Char(help='Expression (tuple) to evaluate the cached key. \nE.g.: "(request.params.get("currency"), )"')
|
||||
|
||||
# Page options
|
||||
header_overlay = fields.Boolean()
|
||||
header_color = fields.Char()
|
||||
@@ -221,36 +218,6 @@ class Page(models.Model):
|
||||
self.ensure_one()
|
||||
return self.view_id.get_website_meta()
|
||||
|
||||
@api.model
|
||||
def _get_cached_blacklist(self):
|
||||
return ('data-snippet="s_website_form"', 'data-no-page-cache=', )
|
||||
|
||||
def _can_be_cached(self, response):
|
||||
""" return False if at least one blacklisted's word is present in content """
|
||||
blacklist = self._get_cached_blacklist()
|
||||
return not any(black in str(response) for black in blacklist)
|
||||
|
||||
def _get_cache_key(self, req):
|
||||
# Always call me with super() AT THE END to have cache_key_expr appended as last element
|
||||
# It is the only way for end user to not use cache via expr.
|
||||
# E.g (None if 'token' in request.params else 1,) will bypass cache_time
|
||||
cache_key = (req.website.id, req.lang, req.httprequest.path)
|
||||
if self.cache_key_expr: # e.g. (request.geoip.get('country_code'),)
|
||||
cache_key += safe_eval(self.cache_key_expr, {'request': req})
|
||||
return cache_key
|
||||
|
||||
def _get_cache_response(self, cache_key):
|
||||
""" Return the cached response corresponding to ``self`` and ``cache_key``.
|
||||
Raise a KeyError if the item is not in cache.
|
||||
"""
|
||||
# HACK: we use the same LRU as ormcache to take advantage from its
|
||||
# distributed invalidation, but we don't explicitly use ormcache
|
||||
return self.pool._Registry__cache[('website.page', _cached_response, self.id, cache_key)]
|
||||
|
||||
def _set_cache_response(self, cache_key, response):
|
||||
""" Put in cache the given response. """
|
||||
self.pool._Registry__cache[('website.page', _cached_response, self.id, cache_key)] = response
|
||||
|
||||
@api.model
|
||||
def _search_get_detail(self, website, order, options):
|
||||
with_description = options['displayDescription']
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tools import mute_logger
|
||||
from odoo.tests.common import HttpCase
|
||||
|
||||
EXTRA_REQUEST = 2 - 1
|
||||
@@ -18,8 +19,10 @@ be added:
|
||||
class UtilPerf(HttpCase):
|
||||
def _get_url_hot_query(self, url, cache=True):
|
||||
url += ('?' not in url and '?' or '')
|
||||
if not cache:
|
||||
url += '&nocache'
|
||||
if cache:
|
||||
url += '&debug='
|
||||
else:
|
||||
url += '&debug=disable-t-cache'
|
||||
|
||||
# ensure worker is in hot state
|
||||
self.url_open(url)
|
||||
@@ -31,6 +34,7 @@ class UtilPerf(HttpCase):
|
||||
|
||||
|
||||
class TestStandardPerformance(UtilPerf):
|
||||
@mute_logger('odoo.http')
|
||||
def test_10_perf_sql_img_controller(self):
|
||||
self.authenticate('demo', 'demo')
|
||||
# not published user, get the not found image placeholder
|
||||
@@ -39,6 +43,7 @@ class TestStandardPerformance(UtilPerf):
|
||||
self.assertEqual(self._get_url_hot_query(url), 6)
|
||||
self.assertEqual(self._get_url_hot_query(url, cache=False), 6)
|
||||
|
||||
@mute_logger('odoo.http')
|
||||
def test_11_perf_sql_img_controller(self):
|
||||
self.authenticate('demo', 'demo')
|
||||
self.env['res.users'].sudo().browse(2).website_published = True
|
||||
@@ -46,6 +51,7 @@ class TestStandardPerformance(UtilPerf):
|
||||
self.assertEqual(self._get_url_hot_query(url), 6)
|
||||
self.assertEqual(self._get_url_hot_query(url, cache=False), 6)
|
||||
|
||||
@mute_logger('odoo.http')
|
||||
def test_20_perf_sql_img_controller_bis(self):
|
||||
url = '/web/image/website/1/favicon'
|
||||
self.assertEqual(self._get_url_hot_query(url), 5)
|
||||
@@ -90,30 +96,30 @@ class TestWebsitePerformance(UtilPerf):
|
||||
def test_10_perf_sql_queries_page(self):
|
||||
# standard untracked website.page
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url), 6)
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url, cache=False), 11)
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url, cache=False), 10)
|
||||
self.menu.unlink()
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url), 6)
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url, cache=False), 11)
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url, cache=False), 10)
|
||||
|
||||
def test_15_perf_sql_queries_page(self):
|
||||
# standard tracked website.page
|
||||
self.page.track = True
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url), 14)
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url, cache=False), 19)
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url, cache=False), 18)
|
||||
self.menu.unlink()
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url), 14)
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url, cache=False), 19)
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url, cache=False), 18)
|
||||
|
||||
def test_20_perf_sql_queries_homepage(self):
|
||||
# homepage "/" has its own controller
|
||||
self.assertEqual(self._get_url_hot_query('/'), 14)
|
||||
self.assertEqual(self._get_url_hot_query('/', cache=False), 18)
|
||||
self.assertEqual(self._get_url_hot_query('/', cache=False), 16)
|
||||
|
||||
def test_30_perf_sql_queries_page_no_layout(self):
|
||||
# website.page with no call to layout templates
|
||||
self.page.arch = '<div>I am a blank page</div>'
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url), 6)
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url, cache=False), 7)
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url), 5)
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url, cache=False), 5)
|
||||
|
||||
def test_40_perf_sql_queries_page_multi_level_menu(self):
|
||||
# menu structure should not impact SQL requests
|
||||
@@ -132,8 +138,9 @@ class TestWebsitePerformance(UtilPerf):
|
||||
menu_aa.parent_id = menu_a
|
||||
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url), 6)
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url, cache=False), 11)
|
||||
self.assertEqual(self._get_url_hot_query(self.page.url, cache=False), 10)
|
||||
|
||||
@mute_logger('odoo.http')
|
||||
def test_50_perf_sql_web_assets(self):
|
||||
# assets route /web/assets/..
|
||||
self.url_open('/') # create assets attachments
|
||||
|
||||
@@ -51,7 +51,11 @@
|
||||
'data-oe-company-name': res_company.name,
|
||||
})"/>
|
||||
</xpath>
|
||||
<xpath expr="//head" position="before">
|
||||
|
||||
<xpath expr="//head" position="attributes">
|
||||
<attribute name="t-cache">main_object if main_object and main_object._name == 'website.page' and not (editable or translatable) else None</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//head/*[1]" position="before">
|
||||
<t t-if="not title">
|
||||
<t t-if="not additional_title and main_object and 'name' in main_object">
|
||||
<t t-set="additional_title" t-value="main_object.name"/>
|
||||
|
||||
@@ -119,8 +119,6 @@
|
||||
<field name="website_indexed"/>
|
||||
<field name="is_published"/>
|
||||
<field name="date_publish"/>
|
||||
<field name="cache_time" groups="base.group_no_one"/>
|
||||
<field name="cache_key_expr" groups="base.group_no_one"/>
|
||||
</group>
|
||||
</group>
|
||||
<label for="menu_ids" string="Related Menu Items"/>
|
||||
|
||||
@@ -13,8 +13,8 @@ class TestBlogPerformance(UtilPerf):
|
||||
self.env['website'].search([]).channel_id = False
|
||||
|
||||
def test_10_perf_sql_blog_standard_data(self):
|
||||
self.assertEqual(self._get_url_hot_query('/blog'), 14)
|
||||
self.assertEqual(self._get_url_hot_query('/blog', cache=False), 14)
|
||||
self.assertEqual(self._get_url_hot_query('/blog'), 11)
|
||||
self.assertEqual(self._get_url_hot_query('/blog', cache=False), 27)
|
||||
|
||||
def test_20_perf_sql_blog_bigger_data_scaling(self):
|
||||
BlogPost = self.env['blog.post']
|
||||
@@ -26,10 +26,10 @@ class TestBlogPerformance(UtilPerf):
|
||||
for blog_post in blog_posts:
|
||||
blog_post.tag_ids += blog_tags
|
||||
blog_tags = blog_tags[:-1]
|
||||
self.assertEqual(self._get_url_hot_query('/blog'), 14)
|
||||
self.assertEqual(self._get_url_hot_query('/blog', cache=False), 14)
|
||||
self.assertEqual(self._get_url_hot_query(blog_post[0].website_url), 28)
|
||||
self.assertEqual(self._get_url_hot_query(blog_post[0].website_url, cache=False), 28)
|
||||
self.assertEqual(self._get_url_hot_query('/blog'), 11)
|
||||
self.assertEqual(self._get_url_hot_query('/blog', cache=False), 34)
|
||||
self.assertEqual(self._get_url_hot_query(blog_post[0].website_url), 26)
|
||||
self.assertEqual(self._get_url_hot_query(blog_post[0].website_url, cache=False), 29)
|
||||
|
||||
def test_30_perf_sql_blog_bigger_data_scaling(self):
|
||||
BlogPost = self.env['blog.post']
|
||||
@@ -42,6 +42,6 @@ class TestBlogPerformance(UtilPerf):
|
||||
blog_post.write({'tag_ids': [[6, 0, random.choices(blog_tags.ids, k=random.randint(0, len(blog_tags)))]]})
|
||||
|
||||
self.assertLessEqual(self._get_url_hot_query('/blog'), 29)
|
||||
self.assertLessEqual(self._get_url_hot_query('/blog', cache=False), 28)
|
||||
self.assertLessEqual(self._get_url_hot_query('/blog', cache=False), 71)
|
||||
self.assertLessEqual(self._get_url_hot_query(blog_post[0].website_url), 34)
|
||||
self.assertLessEqual(self._get_url_hot_query(blog_post[0].website_url, cache=False), 33)
|
||||
|
||||
@@ -5,4 +5,3 @@ from . import account_payment
|
||||
from . import payment_acquirer
|
||||
from . import payment_transaction
|
||||
from . import res_config_settings
|
||||
from . import website_page
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class Page(models.Model):
|
||||
_inherit = 'website.page'
|
||||
|
||||
@api.model
|
||||
def _get_cached_blacklist(self):
|
||||
return super()._get_cached_blacklist() + (
|
||||
# Contains a form with a dynamically added CSRF token
|
||||
'data-snippet="s_donation"',
|
||||
)
|
||||
@@ -332,7 +332,7 @@ class WebsiteSale(http.Controller):
|
||||
categs_domain.append(('id', 'in', search_categories.ids))
|
||||
else:
|
||||
search_categories = Category
|
||||
categs = Category.search(categs_domain)
|
||||
categs = lazy(lambda: Category.search(categs_domain))
|
||||
|
||||
if category:
|
||||
url = "/shop/category/%s" % slug(category)
|
||||
@@ -344,12 +344,12 @@ class WebsiteSale(http.Controller):
|
||||
ProductAttribute = request.env['product.attribute']
|
||||
if products:
|
||||
# get all products without limit
|
||||
attributes = ProductAttribute.search([
|
||||
attributes = lazy(lambda: ProductAttribute.search([
|
||||
('product_tmpl_ids', 'in', search_product.ids),
|
||||
('visibility', '=', 'visible'),
|
||||
])
|
||||
]))
|
||||
else:
|
||||
attributes = ProductAttribute.browse(attributes_ids)
|
||||
attributes = lazy(lambda: ProductAttribute.browse(attributes_ids))
|
||||
|
||||
layout_mode = request.session.get('website_sale_shop_layout_mode')
|
||||
if not layout_mode:
|
||||
|
||||
@@ -22,6 +22,5 @@ from . import sale_order
|
||||
from . import sale_order_line
|
||||
from . import website
|
||||
from . import website_base_unit
|
||||
from . import website_page
|
||||
from . import website_snippet_filter
|
||||
from . import website_visitor
|
||||
|
||||
@@ -273,13 +273,12 @@ class Website(models.Model):
|
||||
self = self.with_company(self.company_id)
|
||||
SaleOrder = self.env['sale.order'].sudo()
|
||||
|
||||
partner_sudo = self.env.user.partner_id
|
||||
sale_order_id = request.session.get('sale_order_id')
|
||||
|
||||
if sale_order_id:
|
||||
sale_order_sudo = SaleOrder.browse(sale_order_id).exists()
|
||||
elif not self.env.user._is_public():
|
||||
sale_order_sudo = partner_sudo.last_website_so_id
|
||||
sale_order_sudo = self.env.user.partner_id.last_website_so_id
|
||||
if sale_order_sudo:
|
||||
available_pricelists = self.get_pricelist_available()
|
||||
if sale_order_sudo.pricelist_id not in available_pricelists:
|
||||
@@ -309,6 +308,8 @@ class Website(models.Model):
|
||||
# Only set when neeeded
|
||||
pricelist_id = False
|
||||
|
||||
partner_sudo = self.env.user.partner_id
|
||||
|
||||
# cart creation was requested
|
||||
if not sale_order_sudo:
|
||||
so_data = self._prepare_sale_order_values(partner_sudo)
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import models
|
||||
from odoo.http import request
|
||||
|
||||
|
||||
class WabsitePage(models.AbstractModel):
|
||||
_inherit = 'website.page'
|
||||
|
||||
def _get_cache_key(self, req):
|
||||
cart = request.website.sale_get_order()
|
||||
cache_key = (cart and cart.cart_quantity or 0,)
|
||||
cache_key += super()._get_cache_key(req)
|
||||
return cache_key
|
||||
@@ -201,6 +201,10 @@ class Lang(models.Model):
|
||||
def _lang_get_id(self, code):
|
||||
return self.with_context(active_test=True).search([('code', '=', code)]).id
|
||||
|
||||
@tools.ormcache('code')
|
||||
def _lang_get_direction(self, code):
|
||||
return self.with_context(active_test=True).search([('code', '=', code)]).direction
|
||||
|
||||
@tools.ormcache('url_code')
|
||||
def _lang_get_code(self, url_code):
|
||||
return self.with_context(active_test=True).search([('url_code', '=', url_code)]).code or url_code
|
||||
|
||||
Reference in New Issue
Block a user