credativ team mailing list archive
-
credativ team
-
Mailing list archive
-
Message #04873
[Merge] lp:~therp-nl/openupgrade-server/6.1-api_ports into lp:openupgrade-server
Holger Brunn (Therp) has proposed merging lp:~therp-nl/openupgrade-server/6.1-api_ports into lp:openupgrade-server.
Requested reviews:
Stefan Rijnhart (Therp) (stefan-therp)
For more details, see:
https://code.launchpad.net/~therp-nl/openupgrade-server/6.1-api_ports/+merge/109645
1&3: ACK
2: Non-namepsaced access to openerp modules is a deprecated feature that is enabled in loading.py:433. As we want to access the openupgrade module in there we have to import it after that if it makes use of the deprecated API.
lp:~therp-nl/openupgrade-server/6.1-api_ports/revision/3984 contains the necessary changes for that, but I don't particularly like it because you have to pay attention to when you import the openupgrade module. And chances are there will be a situation where you want to use it before the openerp namespace is expanded. Further https://bugs.launchpad.net/openobject-addons/+bug/1004539 suggests that it won't take too much time until this expansion is dropped altogether.
--
https://code.launchpad.net/~therp-nl/openupgrade-server/6.1-api_ports/+merge/109645
Your team OpenUpgrade Committers is subscribed to branch lp:openupgrade-server.
=== modified file 'openerp/addons/base/migrations/6.1.1.3/post-migration.py'
--- openerp/addons/base/migrations/6.1.1.3/post-migration.py 2012-03-06 22:36:49 +0000
+++ openerp/addons/base/migrations/6.1.1.3/post-migration.py 2012-06-11 14:20:26 +0000
@@ -24,5 +24,12 @@
openupgrade.load_data(cr, 'base', 'migrations/6.1.1.3/data/base_data.xml')
openupgrade.load_data(cr, 'base', 'migrations/6.1.1.3/data/base_security.xml')
openupgrade.load_data(cr, 'base', 'migrations/6.1.1.3/data/ir.model.access.csv')
+ #force recreating module categories for all categories without xmlid
+ #this fixes addons getting wrong category_ids assigned in case of
+ #multiple categories with the same name
+ cr.execute("""
+ delete from ir_module_category where id not in
+ (select res_id from ir_model_data where model='ir.module.category')
+ """)
except Exception, e:
raise osv.except_osv("OpenUpgrade", '%s: %s' % (me, e))
=== modified file 'openerp/modules/loading.py'
--- openerp/modules/loading.py 2012-05-09 10:48:46 +0000
+++ openerp/modules/loading.py 2012-06-11 14:20:26 +0000
@@ -66,15 +66,6 @@
_logger = logging.getLogger(__name__)
-### OpenUpgrade
-def table_exists(cr, table):
- """ Check whether a certain table or view exists """
- cr.execute(
- 'SELECT count(relname) FROM pg_class WHERE relname = %s',
- (table,))
- return cr.fetchone()[0] == 1
-### End of OpenUpgrade
-
def open_openerp_namespace():
# See comment for open_openerp_namespace.
if openerp.conf.deprecation.open_openerp_namespace:
@@ -252,12 +243,13 @@
return cr.fetchone()[0]
def compare_registries(cr, module):
+ from openerp.openupgrade import openupgrade
"""
OpenUpgrade: Compare the local registry with the global registry,
log any differences and merge the local registry with
the global one.
"""
- if not table_exists(cr, 'openupgrade_record'):
+ if not openupgrade.table_exists(cr, 'openupgrade_record'):
return
for model, fields in local_registry.items():
registry.setdefault(model, {})
@@ -287,6 +279,9 @@
if status is None:
status = {}
+ if skip_modules is None:
+ skip_modules={'pre': [], 'post': []}
+
processed_modules = []
loaded_modules = []
pool = pooler.get_pool(cr.dbname)
@@ -299,14 +294,16 @@
# register, instantiate and initialize models for each modules
for index, package in enumerate(graph):
+
+ if package.name in skip_modules['pre'] and package.name in skip_modules['post']:
+ continue
+
module_name = package.name
module_id = package.id
- if skip_modules and module_name in skip_modules:
- continue
-
_logger.info('module %s: loading objects', package.name)
- migrations.migrate_module(package, 'pre')
+ if package.name not in skip_modules['pre']:
+ migrations.migrate_module(package, 'pre')
load_openerp_module(package.name)
models = pool.load(cr, package)
@@ -366,11 +363,12 @@
# OpenUpgrade: add 'try' block for logging exceptions
# as errors in post scripts seem to be dropped
- try:
- migrations.migrate_module(package, 'post')
- except Exception, e:
- _logger.error('Error executing post migration script for module %s: %s', package, e)
- raise
+ if package.name not in skip_modules['post']:
+ try:
+ migrations.migrate_module(package, 'post')
+ except Exception, e:
+ _logger.error('Error executing post migration script for module %s: %s', package, e)
+ raise
ver = release.major_version + '.' + package.data['version']
# Set new modules and dependencies
@@ -384,6 +382,8 @@
delattr(package, kind)
cr.commit()
+ skip_modules['pre'].append(package.name)
+ skip_modules['post'].append(package.name)
# mark new res_log records as read
cr.execute("update res_log set read=True where create_date >= %s", (dt_before_load,))
@@ -409,13 +409,15 @@
def load_marked_modules(cr, graph, states, force, progressdict, report, loaded_modules, registry):
"""Loads modules marked with ``states``, adding them to ``graph`` and
``loaded_modules`` and returns a list of installed/upgraded modules."""
+ from openerp.openupgrade import openupgrade
processed_modules = []
while True:
cr.execute("SELECT name from ir_module_module WHERE state IN %s" ,(tuple(states),))
module_list = [name for (name,) in cr.fetchall() if name not in graph]
- new_modules_in_graph = graph.add_modules(cr, module_list, force)
+ module_list = openupgrade.add_module_dependencies(cr, module_list)
+ graph.add_modules(cr, module_list, force)
_logger.debug('Updating graph with %d more modules', len(module_list))
- loaded, processed = load_module_graph(cr, graph, progressdict, report=report, skip_modules=loaded_modules, registry=registry)
+ loaded, processed = load_module_graph(cr, graph, progressdict, report=report, skip_modules={'pre': loaded_modules, 'post': loaded_modules}, registry=registry)
processed_modules.extend(processed)
loaded_modules.extend(loaded)
if not processed: break
@@ -511,6 +513,7 @@
states_to_load = ['to install']
processed_install = load_marked_modules(cr, graph, states_to_load, force, status, report, loaded_modules, registry)
processed_modules.extend(processed_install)
+ loaded_modules.extend(processed_install)
else:
processed_install = False
=== modified file 'openerp/openupgrade/openupgrade.py'
--- openerp/openupgrade/openupgrade.py 2012-05-06 18:58:42 +0000
+++ openerp/openupgrade/openupgrade.py 2012-06-11 14:20:26 +0000
@@ -1,24 +1,51 @@
# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# This module copyright (C) 2011-2012 Therp BV (<http://therp.nl>)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
import os
-from osv import osv
+import inspect
+import logging
+import release
+import osv
import pooler
-import logging
import tools
import openupgrade_tools
logger = logging.getLogger('OpenUpgrade')
__all__ = [
+ 'migrate',
'load_data',
'rename_columns',
'rename_tables',
'drop_columns',
'table_exists',
'column_exists',
+ 'logged_query',
'delete_model_workflow',
'set_defaults',
'update_module_names',
'add_ir_model_fields',
+ 'rename_models',
+ 'rename_xmlids',
+ 'get_legacy_name',
]
def load_data(cr, module_name, filename, idref=None, mode='init'):
@@ -83,9 +110,18 @@
def rename_tables(cr, table_spec):
"""
Rename tables. Typically called in the pre script.
- :param column_spec: a list of tuples (old table name, new table name).
+ This function also renames the id sequence if it exists and if it is
+ not modified in the same run.
+
+ :param table_spec: a list of tuples (old table name, new table name).
"""
+ # Append id sequences
+ to_rename = [x[0] for x in table_spec]
+ for old, new in list(table_spec):
+ if (table_exists(cr, old + '_id_seq') and
+ old + '_id_seq' not in to_rename):
+ table_spec.append((old + '_id_seq', new + '_id_seq'))
for (old, new) in table_spec:
logger.info("table %s: renaming to %s",
old, new)
@@ -94,7 +130,7 @@
def rename_models(cr, model_spec):
"""
Rename models. Typically called in the pre script.
- :param column_spec: a list of tuples (old table name, new table name).
+ :param model_spec: a list of tuples (old model name, new model name).
Use case: if a model changes name, but still implements equivalent
functionality you will want to update references in for instance
@@ -106,6 +142,24 @@
old, new)
cr.execute('UPDATE ir_model_fields SET relation = %s '
'WHERE relation = %s', (new, old,))
+ # TODO: signal where the model occurs in references to ir_model
+
+def rename_xmlids(cr, xmlids_spec):
+ """
+ Rename XML IDs. Typically called in the pre script.
+ One usage example is when an ID changes module. In OpenERP 6 for example,
+ a number of res_groups IDs moved to module base from other modules (
+ although they were still being defined in their respective module).
+ """
+ for (old, new) in xmlids_spec:
+ if not old.split('.') or not new.split('.'):
+ logger.error(
+ 'Cannot rename XMLID %s to %s: need the module '
+ 'reference to be specified in the IDs' % (old, new))
+ else:
+ query = ("UPDATE ir_model_data SET module = %s, name = %s "
+ "WHERE module = %s and name = %s")
+ logged_query(cr, query, tuple(new.split('.') + old.split('.')))
def drop_columns(cr, column_spec):
"""
@@ -161,8 +215,8 @@
"""
def write_value(ids, field, value):
- logger.info("model %s, field %s: setting default value of %d resources to %s",
- model, field, len(ids), unicode(value))
+ logger.debug("model %s, field %s: setting default value of resources %s to %s",
+ model, field, ids, unicode(value))
obj.write(cr, 1, ids, {field: value})
for model in default_spec.keys():
@@ -170,40 +224,42 @@
if not obj:
raise osv.except_osv("Migration: error setting default, no such model: %s" % model, "")
- for field, value in default_spec[model]:
- domain = not force and [(field, '=', False)] or []
- ids = obj.search(cr, 1, domain)
- if not ids:
- continue
- if value is None:
- # Set the value by calling the _defaults of the object.
- # Typically used for company_id on various models, and in that
- # case the result depends on the user associated with the object.
- # We retrieve create_uid for this purpose and need to call the _defaults
- # function per resource. Otherwise, write all resources at once.
- if field in obj._defaults:
- if not callable(obj._defaults[field]):
- write_value(ids, field, obj._defaults[field])
+ for field, value in default_spec[model]:
+ domain = not force and [(field, '=', False)] or []
+ ids = obj.search(cr, 1, domain)
+ if not ids:
+ continue
+ if value is None:
+ # Set the value by calling the _defaults of the object.
+ # Typically used for company_id on various models, and in that
+ # case the result depends on the user associated with the object.
+ # We retrieve create_uid for this purpose and need to call the _defaults
+ # function per resource. Otherwise, write all resources at once.
+ if field in obj._defaults:
+ if not callable(obj._defaults[field]):
+ write_value(ids, field, obj._defaults[field])
+ else:
+ # existence users is covered by foreign keys, so this is not needed
+ # cr.execute("SELECT %s.id, res_users.id FROM %s LEFT OUTER JOIN res_users ON (%s.create_uid = res_users.id) WHERE %s.id IN %s" %
+ # (obj._table, obj._table, obj._table, obj._table, tuple(ids),))
+ cr.execute("SELECT id, COALESCE(create_uid, 1) FROM %s " % obj._table + "WHERE id in %s", (tuple(ids),))
+ # Execute the function once per user_id
+ user_id_map = {}
+ for row in cr.fetchall():
+ user_id_map.setdefault(row[1], []).append(row[0])
+ for user_id in user_id_map:
+ write_value(
+ user_id_map[user_id], field,
+ obj._defaults[field](obj, cr, user_id, None))
else:
- # existence users is covered by foreign keys, so this is not needed
- # cr.execute("SELECT %s.id, res_users.id FROM %s LEFT OUTER JOIN res_users ON (%s.create_uid = res_users.id) WHERE %s.id IN %s" %
- # (obj._table, obj._table, obj._table, obj._table, tuple(ids),))
- cr.execute("SELECT id, COALESCE(create_uid, 1) FROM %s " % obj._table + "WHERE id in %s", (tuple(ids),))
- fetchdict = dict(cr.fetchall())
- for id in ids:
- write_value([id], field, obj._defaults[field](obj, cr, fetchdict.get(id, 1), None))
- if id not in fetchdict:
- logger.info("model %s, field %s, id %d: no create_uid defined or user does not exist anymore",
- model, field, id)
+ error = ("OpenUpgrade: error setting default, field %s with "
+ "None default value not in %s' _defaults" % (
+ field, model))
+ logger.error(error)
+ # this exeption seems to get lost in a higher up try block
+ osv.except_osv("OpenUpgrade", error)
else:
- error = ("OpenUpgrade: error setting default, field %s with "
- "None default value not in %s' _defaults" % (
- field, model))
- logger.error(error)
- # this exeption seems to get lost in a higher up try block
- osv.except_osv("OpenUpgrade", error)
- else:
- write_value(ids, field, value)
+ write_value(ids, field, value)
def logged_query(cr, query, args=None):
if args is None:
@@ -257,3 +313,74 @@
query = 'ALTER TABLE ir_model_fields ADD COLUMN %s %s' % (
column)
logged_query(cr, query, [])
+
+def get_legacy_name(original_name):
+ """
+ Returns a versioned name for legacy tables/columns/etc
+ Use this function instead of some custom name to avoid
+ collisions with future or past legacy tables/columns/etc
+
+ :param original_name: the original name of the column
+ :param version: current version as passed to migrate()
+ """
+ return 'openupgrade_legacy_'+('_').join(map(str, release.version_info))+'_'+original_name
+
+def add_module_dependencies(cr, module_list):
+ """
+ Select (new) dependencies from the modules in the list
+ so that we can inject them into the graph at upgrade
+ time. Used in the modified OpenUpgrade Server,
+ not to be used in migration scripts
+ """
+ if not module_list:
+ return module_list
+ cr.execute("""
+ SELECT ir_module_module_dependency.name
+ FROM
+ ir_module_module,
+ ir_module_module_dependency
+ WHERE
+ module_id = ir_module_module.id
+ AND ir_module_module.name in %s
+ """, (tuple(module_list),))
+ dependencies = [x[0] for x in cr.fetchall()]
+ return list(set(module_list + dependencies))
+
+def migrate():
+ """
+ This is the decorator for the migrate() function
+ in migration scripts.
+ Return when the 'version' argument is not defined,
+ and log execeptions.
+ Retrieve debug context data from the frame above for
+ logging purposes.
+ """
+ def wrap(func):
+ def wrapped_function(cr, version):
+ stage = 'unknown'
+ module = 'unknown'
+ filename = 'unknown'
+ try:
+ frame = inspect.getargvalues(inspect.stack()[1][0])
+ stage = frame.locals['stage']
+ module = frame.locals['pkg'].name
+ filename = frame.locals['fp'].name
+ except Exception, e:
+ logger.error(
+ "'migrate' decorator: failed to inspect "
+ "the frame above: %s" % e)
+ pass
+ if not version:
+ return
+ logger.info(
+ "%s: %s-migration script called with version %s" %
+ (module, stage, version))
+ try:
+ # The actual function is called here
+ func(cr, version)
+ except Exception, e:
+ logger.error(
+ "%s: error in migration script %s: %s" %
+ (module, filename, e))
+ return wrapped_function
+ return wrap
Follow ups