From 42625ce02bc33eb965afd177e492613cdb4da327 Mon Sep 17 00:00:00 2001
From: Vincent Pelletier <vincent@nexedi.com>
Date: Mon, 25 Jul 2011 13:34:42 +0000
Subject: [PATCH] Factorise code for auto-generated related keys.

Also, in RelatedBaseCategory, move most computation cost from call-time to
construction-time.
---
 product/ERP5Catalog/CatalogTool.py | 113 ++++++++++++++---------------
 1 file changed, 56 insertions(+), 57 deletions(-)

diff --git a/product/ERP5Catalog/CatalogTool.py b/product/ERP5Catalog/CatalogTool.py
index 0f80d788d5..621832ba5e 100644
--- a/product/ERP5Catalog/CatalogTool.py
+++ b/product/ERP5Catalog/CatalogTool.py
@@ -61,8 +61,12 @@ from Persistence import Persistent
 from Acquisition import Implicit
 
 DYNAMIC_METHOD_NAME = 'z_related_'
+DYNAMIC_METHOD_NAME_LEN = len(DYNAMIC_METHOD_NAME)
 STRICT_DYNAMIC_METHOD_NAME = DYNAMIC_METHOD_NAME + 'strict_'
+STRICT_DYNAMIC_METHOD_NAME_LEN = len(STRICT_DYNAMIC_METHOD_NAME)
 RELATED_DYNAMIC_METHOD_NAME = '_related'
+# Negative as it's used as a slice end offset
+RELATED_DYNAMIC_METHOD_NAME_LEN = -len(RELATED_DYNAMIC_METHOD_NAME)
 ZOPE_SECURITY_SUFFIX = '__roles__'
 
 class IndexableObjectWrapper(object):
@@ -190,27 +194,39 @@ class RelatedBaseCategory(Method):
     """
     def __init__(self, id, strict_membership=0, related=0):
       self._id = id
-      self.strict_membership=strict_membership
-      self.related = related
+      if strict_membership:
+        strict = 'AND %(category_table)s.category_strict_membership = 1\n'
+      else:
+        strict = ''
+      # From the point of view of query_table, we are looking up objects...
+      if related:
+        # ... which have a relation toward us
+        from_table = 'related'
+        to_table = 'relation_holder'
+      else:
+        # ... toward which we have a relation
+        from_table = 'relation_holder'
+        to_table = 'related'
+      self._template = """\
+%%(category_table)s.base_category_uid = %%(base_category_uid)s
+%(strict)sAND %%(%(to_table)s)s.uid = %%(category_table)s.category_uid
+AND %%(category_table)s.uid = %%(%(from_table)s)s.uid""" % {
+          'strict': strict,
+          'from_table': from_table,
+          'to_table': to_table,
+      }
 
     def __call__(self, instance, table_0, table_1, query_table='catalog', **kw):
       """Create the sql code for this related key."""
-      base_category_uid = instance.portal_categories._getOb(self._id).getUid()
-      expression_list = []
-      append = expression_list.append
-      if self.related:
-        append('%s.uid = %s.uid' % (table_1,table_0))
-        if self.strict_membership:
-          append('AND %s.category_strict_membership = 1' % table_0)
-        append('AND %s.base_category_uid = %s' % (table_0,base_category_uid))
-        append('AND %s.category_uid = %s.uid' % (table_0,query_table))
-      else:
-        append('%s.uid = %s.category_uid' % (table_1,table_0))
-        if self.strict_membership:
-          append('AND %s.category_strict_membership = 1' % table_0)
-        append('AND %s.base_category_uid = %s' % (table_0,base_category_uid))
-        append('AND %s.uid = %s.uid' % (table_0,query_table))
-      return ' '.join(expression_list)
+      # Note: in normal conditions, our category's uid will not change from
+      # one invocation to the next.
+      return self._template % {
+        'base_category_uid': instance.getPortalObject().portal_categories.\
+          _getOb(self._id).getUid(),
+        'category_table': table_0,
+        'relation_holder': query_table,
+        'related': table_1,
+      }
 
 class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject):
     """
@@ -889,31 +905,19 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject):
 
             if end_key.startswith('related_'):
               end_key = end_key[len('related_'):]
-              # accept only some catalog columns
-              if end_key in ('title', 'uid', 'description', 'reference',
-                             'relative_url', 'id', 'portal_type',
-                             'simulation_state'):
-                if strict:
-                  related_key_list.append(
-                        '%s%s | category,catalog/%s/z_related_strict_%s_related' %
-                        (prefix, key, end_key, expected_base_cat_id))
-                else:
-                  related_key_list.append(
-                        '%s%s | category,catalog/%s/z_related_%s_related' %
-                        (prefix, key, end_key, expected_base_cat_id))
+              suffix = '_related'
             else:
-              # accept only some catalog columns
-              if end_key in ('title', 'uid', 'description', 'reference',
-                             'relative_url', 'id', 'portal_type',
-                             'simulation_state'):
-                if strict:
-                  related_key_list.append(
-                        '%s%s | category,catalog/%s/z_related_strict_%s' %
-                        (prefix, key, end_key, expected_base_cat_id))
-                else:
-                  related_key_list.append(
-                        '%s%s | category,catalog/%s/z_related_%s' %
-                        (prefix, key, end_key, expected_base_cat_id))
+              suffix = ''
+            # accept only some catalog columns
+            if end_key in ('title', 'uid', 'description', 'reference',
+                           'relative_url', 'id', 'portal_type',
+                           'simulation_state'):
+              if strict:
+                pattern = '%s%s | category,catalog/%s/z_related_strict_%s%s'
+              else:
+                pattern = '%s%s | category,catalog/%s/z_related_%s%s'
+              related_key_list.append(pattern %
+                (prefix, key, end_key, expected_base_cat_id, suffix))
 
       return related_key_list
 
@@ -925,23 +929,18 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject):
       result = None
       if name.startswith(DYNAMIC_METHOD_NAME) and \
           not name.endswith(ZOPE_SECURITY_SUFFIX):
-
+        kw = {}
         if name.endswith(RELATED_DYNAMIC_METHOD_NAME):
-          if name.startswith(STRICT_DYNAMIC_METHOD_NAME):
-            base_category_id = name[len(STRICT_DYNAMIC_METHOD_NAME):-len('_related')]
-            method = RelatedBaseCategory(base_category_id,
-                                         strict_membership=1, related=1)
-          else:
-            base_category_id = name[len(DYNAMIC_METHOD_NAME):-len('_related')]
-            method = RelatedBaseCategory(base_category_id, related=1)
+          end_offset = RELATED_DYNAMIC_METHOD_NAME_LEN
+          kw['related'] = 1
         else:
-          if name.startswith(STRICT_DYNAMIC_METHOD_NAME):
-            base_category_id = name[len(STRICT_DYNAMIC_METHOD_NAME):]
-            method = RelatedBaseCategory(base_category_id, strict_membership=1)
-          else:
-            base_category_id = name[len(DYNAMIC_METHOD_NAME):]
-            method = RelatedBaseCategory(base_category_id)
-
+          end_offset = None
+        if name.startswith(STRICT_DYNAMIC_METHOD_NAME):
+          start_offset = STRICT_DYNAMIC_METHOD_NAME_LEN
+          kw['strict_membership'] = 1
+        else:
+          start_offset = DYNAMIC_METHOD_NAME_LEN
+        method = RelatedBaseCategory(name[start_offset:end_offset], **kw)
         setattr(self.__class__, name, method)
         # This getattr has 2 purposes:
         # - wrap in acquisition context
-- 
2.30.9