1
# -*- coding: utf-8 -*-
7
import openupgrade_tools
9
logger = logging.getLogger('OpenUpgrade')
18
'delete_model_workflow',
20
'update_module_names',
21
'add_ir_model_fields',
24
def load_data(cr, module_name, filename, idref=None, mode='init'):
26
Load an xml or csv data file from your post script. The usual case for this is the
27
occurrence of newly added essential or useful data in the module that is
28
marked with "noupdate='1'" and without "forcecreate='1'" so that it will
29
not be loaded by the usual upgrade mechanism. Leaving the 'mode' argument to
30
its default 'init' will load the data from your migration script.
32
Theoretically, you could simply load a stock file from the module, but be
33
careful not to reinitialize any data that could have been customized.
34
Preferably, select only the newly added items. Copy these to a file
35
in your migrations directory and load that file.
36
Leave it to the user to actually delete existing resources that are
37
marked with 'noupdate' (other named items will be deleted
41
:param module_name: the name of the module
42
:param filename: the path to the filename, relative to the module \
44
:param idref: optional hash with ?id mapping cache?
45
:param mode: one of 'init', 'update', 'demo'. Always use 'init' for adding new items \
46
from files that are marked with 'noupdate'. Defaults to 'init'.
52
logger.info('%s: loading %s' % (module_name, filename))
53
_, ext = os.path.splitext(filename)
54
pathname = os.path.join(module_name, filename)
55
fp = tools.file_open(pathname)
59
tools.convert_csv_import(cr, module_name, pathname, fp.read(), idref, mode, noupdate)
61
tools.convert_xml_import(cr, module_name, fp, idref, mode=mode)
65
# for backwards compatibility
67
table_exists = openupgrade_tools.table_exists
69
def rename_columns(cr, column_spec):
71
Rename table columns. Typically called in the pre script.
73
:param column_spec: a hash with table keys, with lists of tuples as values. \
74
Tuples consist of (old_name, new_name).
77
for table in column_spec.keys():
78
for (old, new) in column_spec[table]:
79
logger.info("table %s, column %s: renaming to %s",
81
cr.execute('ALTER TABLE "%s" RENAME "%s" TO "%s"' % (table, old, new,))
83
def rename_tables(cr, table_spec):
85
Rename tables. Typically called in the pre script.
86
:param column_spec: a list of tuples (old table name, new table name).
89
for (old, new) in table_spec:
90
logger.info("table %s: renaming to %s",
92
cr.execute('ALTER TABLE "%s" RENAME TO "%s"' % (old, new,))
94
def rename_models(cr, model_spec):
96
Rename models. Typically called in the pre script.
97
:param column_spec: a list of tuples (old table name, new table name).
99
Use case: if a model changes name, but still implements equivalent
100
functionality you will want to update references in for instance
104
for (old, new) in model_spec:
105
logger.info("model %s: renaming to %s",
107
cr.execute('UPDATE ir_model_fields SET relation = %s '
108
'WHERE relation = %s', (new, old,))
110
def drop_columns(cr, column_spec):
112
Drop columns but perform an additional check if a column exists.
113
This covers the case of function fields that may or may not be stored.
114
Consider that this may not be obvious: an additional module can govern
115
a function fields' store properties.
117
:param column_spec: a list of (table, column) tuples
119
for (table, column) in column_spec:
120
logger.info("table %s: drop column %s",
122
if column_exists(cr, table, column):
123
cr.execute('ALTER TABLE "%s" DROP COLUMN "%s"' %
126
logger.warn("table %s: column %s did not exist",
129
def delete_model_workflow(cr, model):
131
Forcefully remove active workflows for obsolete models,
132
to prevent foreign key issues when the orm deletes the model.
136
"DELETE FROM wkf_workitem WHERE act_id in "
137
"( SELECT wkf_activity.id "
138
" FROM wkf_activity, wkf "
139
" WHERE wkf_id = wkf.id AND "
144
"DELETE FROM wkf WHERE osv = %s", (model,))
146
def set_defaults(cr, pool, default_spec, force=False):
148
Set default value. Useful for fields that are newly required. Uses orm, so
149
call from the post script.
151
:param default_spec: a hash with model names as keys. Values are lists of \
152
tuples (field, value). None as a value has a special meaning: it assigns \
153
the default value. If this value is provided by a function, the function is \
154
called as the user that created the resource.
155
:param force: overwrite existing values. To be used for assigning a non- \
156
default value (presumably in the case of a new column). The ORM assigns \
157
the default value as declared in the model in an earlier stage of the \
158
process. Beware of issues with resources loaded from new data that \
159
actually do require the model's default, in combination with the post \
160
script possible being run multiple times.
163
def write_value(ids, field, value):
164
logger.info("model %s, field %s: setting default value of %d resources to %s",
165
model, field, len(ids), unicode(value))
166
obj.write(cr, 1, ids, {field: value})
168
for model in default_spec.keys():
169
obj = pool.get(model)
171
raise osv.except_osv("Migration: error setting default, no such model: %s" % model, "")
173
for field, value in default_spec[model]:
174
domain = not force and [(field, '=', False)] or []
175
ids = obj.search(cr, 1, domain)
179
# Set the value by calling the _defaults of the object.
180
# Typically used for company_id on various models, and in that
181
# case the result depends on the user associated with the object.
182
# We retrieve create_uid for this purpose and need to call the _defaults
183
# function per resource. Otherwise, write all resources at once.
184
if field in obj._defaults:
185
if not callable(obj._defaults[field]):
186
write_value(ids, field, obj._defaults[field])
188
# existence users is covered by foreign keys, so this is not needed
189
# 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" %
190
# (obj._table, obj._table, obj._table, obj._table, tuple(ids),))
191
cr.execute("SELECT id, COALESCE(create_uid, 1) FROM %s " % obj._table + "WHERE id in %s", (tuple(ids),))
192
fetchdict = dict(cr.fetchall())
194
write_value([id], field, obj._defaults[field](obj, cr, fetchdict.get(id, 1), None))
195
if id not in fetchdict:
196
logger.info("model %s, field %s, id %d: no create_uid defined or user does not exist anymore",
199
error = ("OpenUpgrade: error setting default, field %s with "
200
"None default value not in %s' _defaults" % (
203
# this exeption seems to get lost in a higher up try block
204
osv.except_osv("OpenUpgrade", error)
206
write_value(ids, field, value)
208
def logged_query(cr, query, args=None):
211
res = cr.execute(query, args)
212
logger.debug('Running %s', query)
215
logger.warn('No rows affected for query "%s"', query)
218
def column_exists(cr, table, column):
219
""" Check whether a certain column exists """
221
'SELECT count(attname) FROM pg_attribute '
223
'( SELECT oid FROM pg_class WHERE relname = %s ) '
226
return cr.fetchone()[0] == 1
228
def update_module_names(cr, namespec):
230
Deal with changed module names of certified modules
231
in order to prevent 'certificate not unique' error,
232
as well as updating the module reference in the
235
:param namespec: tuple of (old name, new name)
237
for (old_name, new_name) in namespec:
238
query = ("UPDATE ir_module_module SET name = %s "
240
logged_query(cr, query, (new_name, old_name))
241
query = ("UPDATE ir_model_data SET module = %s "
242
"WHERE module = %s ")
243
logged_query(cr, query, (new_name, old_name))
245
def add_ir_model_fields(cr, columnspec):
247
Typically, new columns on ir_model_fields need to be added in a very
248
early stage in the upgrade process of the base module, in raw sql
249
as they need to be in place before any model gets initialized.
250
Do not use for fields with additional SQL constraints, such as a
251
reference to another table or the cascade constraint, but craft your
252
own statement taking them into account.
254
:param columnspec: tuple of (column name, column type)
256
for column in columnspec:
257
query = 'ALTER TABLE ir_model_fields ADD COLUMN %s %s' % (
259
logged_query(cr, query, [])