Commit 9a9705cf authored by Yusei Tahara's avatar Yusei Tahara

Improve field performance. This changes depends on ERP5Type.Interactor.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@16501 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 58b44185
This diff is collapsed.
...@@ -31,7 +31,9 @@ from Products.Formulator import Widget, Validator ...@@ -31,7 +31,9 @@ from Products.Formulator import Widget, Validator
from Products.Formulator.Field import ZMIField from Products.Formulator.Field import ZMIField
from Products.Formulator.DummyField import fields from Products.Formulator.DummyField import fields
from Products.Formulator.Errors import ValidationError from Products.Formulator.Errors import ValidationError
from Products.Formulator import MethodField
from Products.ERP5Type.Utils import convertToUpperCase from Products.ERP5Type.Utils import convertToUpperCase
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
from Products.CMFCore.utils import getToolByName from Products.CMFCore.utils import getToolByName
from Products.PageTemplates.PageTemplateFile import PageTemplateFile from Products.PageTemplates.PageTemplateFile import PageTemplateFile
...@@ -44,10 +46,17 @@ from Products.PythonScripts.standard import url_quote_plus ...@@ -44,10 +46,17 @@ from Products.PythonScripts.standard import url_quote_plus
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from MethodObject import Method from MethodObject import Method
from zLOG import LOG, WARNING, DEBUG from zLOG import LOG, WARNING, DEBUG, PROBLEM
from Acquisition import aq_base, aq_inner, aq_acquire, aq_chain from Acquisition import aq_base, aq_inner, aq_acquire, aq_chain
from Globals import DTMLFile from Globals import DTMLFile
from Products.Formulator.TALESField import TALESMethod
from Products.ERP5Form.ListBox import ListBox
from Products.ERP5Form.Form import StaticValue, TALESValue, OverrideValue, DefaultValue, EditableValue
_field_value_cache = {}
def purgeFieldValueCache():
_field_value_cache.clear()
class WidgetDelegatedMethod(Method): class WidgetDelegatedMethod(Method):
"""Method delegated to the proxied field's widget. """Method delegated to the proxied field's widget.
...@@ -324,6 +333,11 @@ class ProxyField(ZMIField): ...@@ -324,6 +333,11 @@ class ProxyField(ZMIField):
""" """
Return template field of the proxy field. Return template field of the proxy field.
""" """
try:
return self._getTemplateFieldCache()
except KeyError:
pass
form = self.aq_parent form = self.aq_parent
object = form.aq_parent object = form.aq_parent
try: try:
...@@ -335,6 +349,7 @@ class ProxyField(ZMIField): ...@@ -335,6 +349,7 @@ class ProxyField(ZMIField):
'Could not get a field from a proxy field %s in %s' % \ 'Could not get a field from a proxy field %s in %s' % \
(self.id, object.id)) (self.id, object.id))
proxy_field = None proxy_field = None
self._setTemplateFieldCache(proxy_field)
return proxy_field return proxy_field
def getRecursiveTemplateField(self): def getRecursiveTemplateField(self):
...@@ -342,11 +357,13 @@ class ProxyField(ZMIField): ...@@ -342,11 +357,13 @@ class ProxyField(ZMIField):
Return template field of the proxy field. Return template field of the proxy field.
This result must not be a ProxyField. This result must not be a ProxyField.
""" """
template_field = self.getTemplateField() field = self
if template_field.__class__ == ProxyField: while True:
return template_field.getRecursiveTemplateField() template_field = field.getTemplateField()
else: if template_field.__class__ != ProxyField:
return template_field break
field = template_field
return template_field
security.declareProtected('Access contents information', security.declareProtected('Access contents information',
'is_delegated') 'is_delegated')
...@@ -439,26 +456,6 @@ class ProxyField(ZMIField): ...@@ -439,26 +456,6 @@ class ProxyField(ZMIField):
# ("form_id and field_id don't define a valid template") # ("form_id and field_id don't define a valid template")
pass pass
security.declareProtected('Access contents information', 'get_value')
def get_value(self, id, **kw):
"""Get value for id.
Optionally pass keyword arguments that get passed to TALES
expression.
"""
result = None
if (id in self.widget.property_names) or \
(not self.is_delegated(id)):
result = ZMIField.get_value(self, id, **kw)
else:
proxy_field = self.getTemplateField()
if proxy_field is not None:
REQUEST = get_request()
REQUEST.set('field__proxyfield_%s_%s' % (proxy_field.id, id),
REQUEST.get('field__proxyfield_%s_%s' % (self.id, id), self))
result = proxy_field.get_value(id, **kw)
return result
security.declareProtected('Access contents information', 'has_value') security.declareProtected('Access contents information', 'has_value')
def has_value(self, id): def has_value(self, id):
""" """
...@@ -485,3 +482,115 @@ class ProxyField(ZMIField): ...@@ -485,3 +482,115 @@ class ProxyField(ZMIField):
else: else:
result = ZMIField._get_user_input_value(self, key, REQUEST) result = ZMIField._get_user_input_value(self, key, REQUEST)
return result return result
#
# Performance improvement
#
def get_tales_expression(self, id):
field = self
while True:
if (id in field.widget.property_names or
not field.is_delegated(id)):
tales = field.get_tales(id)
if tales:
return TALESMethod(tales._text)
else:
return None
proxied_field = field.getTemplateField()
if proxied_field.__class__ == ProxyField:
field = proxied_field
elif proxied_field is None:
raise ValueError, "Can't find the template field of %s" % self.id
else:
tales = proxied_field.get_tales(id)
if tales:
return TALESMethod(tales._text)
else:
return None
def getFieldValue(self, field, id, **kw):
"""
Return a callable expression
"""
tales_expr = self.get_tales_expression(id)
if tales_expr:
return TALESValue(tales_expr)
# FIXME: backwards compat hack to make sure overrides dict exists
if not hasattr(self, 'overrides'):
self.overrides = {}
override = self.overrides.get(id, "")
if override:
return OverrideValue(override)
# Get a normal value.
try:
template_field = self.getRecursiveTemplateField()
# Old ListBox instance might have default attribute. so we need to check it.
if id=='default' and isinstance(aq_base(template_field), ListBox):
return self._get_value(id, **kw)
value = self.get_recursive_orig_value(id)
except KeyError:
# For ListBox
return self._get_value(id, **kw)
field_id = field.id
if id == 'default' and field_id.startswith('my_'):
return DefaultValue(field_id, value)
# For the 'editable' value, we try to get a default value
if id == 'editable':
return EditableValue(value)
# Return default value in non callable mode
if callable(value):
return StaticValue(value)
# Return default value in non callable mode
return StaticValue(value)(field, id, **kw)
security.declareProtected('Access contents information', 'get_value')
def get_value(self, id, **kw):
REQUEST = get_request()
if ((id in self.widget.property_names) or
(not self.is_delegated(id))):
return ZMIField.get_value(self, id, **kw)
field = self
proxy_field = self.getTemplateField()
if proxy_field is not None and REQUEST is not None:
field = REQUEST.get('field__proxyfield_%s_%s' % (self.id, id), self)
REQUEST.set('field__proxyfield_%s_%s' % (proxy_field.id, id), field)
cache_id = ('ProxyField.get_value',
self._p_oid or repr(self),
field._p_oid or repr(field),
id)
try:
value = _field_value_cache[cache_id]
except KeyError:
# either returns non callable value (ex. "Title")
# or a FieldValue instance of appropriate class
value = _field_value_cache[cache_id] = self.getFieldValue(field, id, **kw)
if callable(value):
return value(field, id, **kw)
return value
def _get_value(self, id, **kw):
proxy_field = self.getTemplateField()
if proxy_field is not None:
return proxy_field.get_value(id, **kw)
def _getCacheId(self):
return '%s%s' % ('ProxyField', self._p_oid or repr(self))
def _setTemplateFieldCache(self, field):
getTransactionalVariable(self)[self._getCacheId()] = field
def _getTemplateFieldCache(self):
return getTransactionalVariable(self)[self._getCacheId()].__of__(self.aq_parent)
...@@ -53,7 +53,7 @@ from Products.Formulator.StandardFields import FloatField ...@@ -53,7 +53,7 @@ from Products.Formulator.StandardFields import FloatField
from Products.Formulator.StandardFields import StringField from Products.Formulator.StandardFields import StringField
from Products.ERP5Type.Core.Folder import Folder from Products.ERP5Type.Core.Folder import Folder
from Products.ERP5Form.Form import ERP5Form from Products.ERP5Form.Form import ERP5Form, purgeFieldValueCache
class TestFloatField(unittest.TestCase): class TestFloatField(unittest.TestCase):
...@@ -75,6 +75,7 @@ class TestFloatField(unittest.TestCase): ...@@ -75,6 +75,7 @@ class TestFloatField(unittest.TestCase):
self.field.values['precision'] = 0 self.field.values['precision'] = 0
self.assertEquals('12', self.widget.format_value(self.field, 12.34)) self.assertEquals('12', self.widget.format_value(self.field, 12.34))
purgeFieldValueCache() # call this before changing internal field values.
self.field.values['precision'] = 2 self.field.values['precision'] = 2
self.assertEquals('0.01', self.widget.format_value(self.field, 0.011)) self.assertEquals('0.01', self.widget.format_value(self.field, 0.011))
# value is rounded # value is rounded
......
...@@ -50,6 +50,7 @@ ZopeTestCase.installProduct('ERP5Form') ...@@ -50,6 +50,7 @@ ZopeTestCase.installProduct('ERP5Form')
from Products.Formulator.TALESField import TALESMethod from Products.Formulator.TALESField import TALESMethod
from Products.ERP5Type.Core.Folder import Folder from Products.ERP5Type.Core.Folder import Folder
from Products.ERP5Form.Form import ERP5Form from Products.ERP5Form.Form import ERP5Form
from Products.ERP5Form.ProxyField import purgeFieldValueCache
class TestProxify(unittest.TestCase): class TestProxify(unittest.TestCase):
...@@ -103,6 +104,7 @@ class TestProxify(unittest.TestCase): ...@@ -103,6 +104,7 @@ class TestProxify(unittest.TestCase):
self.assertEqual(field.is_delegated('description'), True) self.assertEqual(field.is_delegated('description'), True)
self.assertEqual(field.get_value('description'), '') self.assertEqual(field.get_value('description'), '')
purgeFieldValueCache() # must purge cache before changing internal field value.
template_field = self.base_view.my_string_field template_field = self.base_view.my_string_field
template_field.values['description'] = 'Description' template_field.values['description'] = 'Description'
self.assertEqual(field.get_value('description'), 'Description') self.assertEqual(field.get_value('description'), 'Description')
...@@ -131,6 +133,7 @@ class TestProxify(unittest.TestCase): ...@@ -131,6 +133,7 @@ class TestProxify(unittest.TestCase):
self.assertEqual(field.has_value('scrap_variable'), 0) self.assertEqual(field.has_value('scrap_variable'), 0)
purgeFieldValueCache() # must purge cache before changing internal field value.
template_field = self.address_view.my_region template_field = self.address_view.my_region
template_field.values['title'] = 'Region' template_field.values['title'] = 'Region'
self.assertEqual(field.get_value('title'), 'Region') self.assertEqual(field.get_value('title'), 'Region')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment