Skip to content

Commit de1b261

Browse files
authored
Merge pull request #412 from ForgeFlow/master-imp-add_columns
[ADD] add_column: split from add_fields
2 parents 827b5ac + 5247631 commit de1b261

1 file changed

Lines changed: 118 additions & 45 deletions

File tree

openupgradelib/openupgrade.py

Lines changed: 118 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ def do_raise(error):
163163
"migrate",
164164
"logging",
165165
"load_data",
166+
"add_columns",
166167
"add_fields",
167168
"copy_columns",
168169
"copy_fields_multilang",
@@ -191,6 +192,7 @@ def do_raise(error):
191192
"add_ir_model_fields",
192193
"get_legacy_name",
193194
"get_model2table",
195+
"get_field2column_type",
194196
"m2o_to_x2m",
195197
"float_to_integer",
196198
"message",
@@ -1912,6 +1914,44 @@ def get_model2table(model):
19121914
return model2table.get(model, model.replace(".", "_"))
19131915

19141916

1917+
def get_field2column_type(field_type, translatable=False):
1918+
"""This method returns SQL type given a field type.
1919+
1920+
:param: field type: binary, boolean, char, date, datetime, float, html,
1921+
integer, many2many, many2one, many2one_reference, monetary, one2many,
1922+
reference, selection, text, serialized. The list can vary depending on
1923+
Odoo version or custom added field types.
1924+
:param: (optional) translatable: From >=v16, if field is translatable then
1925+
SQL field type is changed to 'jsonb'.
1926+
:return: SQL field type: If the field type is custom or if it's one of the special
1927+
cases (see below), you need to indicate here the SQL type to use
1928+
(from the valid PostgreSQL types):
1929+
https://www.postgresql.org/docs/9.6/static/datatype.html"""
1930+
sql_type_mapping = {
1931+
"binary": "bytea", # If there's attachment, no SQL. Force it manually
1932+
"boolean": "bool",
1933+
"char": "varchar", # Force it manually if there's size limit
1934+
"date": "date",
1935+
"datetime": "timestamp",
1936+
"float": "numeric", # Force manually to double precision if no digits
1937+
"html": "text",
1938+
"integer": "int4",
1939+
"many2many": False, # No need to create SQL column
1940+
"many2one": "int4",
1941+
"many2one_reference": "int4",
1942+
"monetary": "numeric",
1943+
"one2many": False, # No need to create SQL column
1944+
"reference": "varchar",
1945+
"selection": "varchar", # Can be sometimes integer. Force it manually
1946+
"text": "text",
1947+
"serialized": "text",
1948+
"json": "jsonb",
1949+
}
1950+
if version_info[0] > 15 and field_type in ["char", "text", "html"] and translatable:
1951+
return "jsonb"
1952+
return sql_type_mapping.get(field_type, False)
1953+
1954+
19151955
def m2o_to_x2m(cr, model, table, field, source_field):
19161956
"""
19171957
Transform many2one relations into one2many or many2many.
@@ -2995,6 +3035,71 @@ def format_message(f):
29953035
f.active = False
29963036

29973037

3038+
def add_columns(env, field_spec):
3039+
"""This method adds a new column.
3040+
3041+
It's intended for being run in pre-migration scripts for pre-populating
3042+
columns to avoid the execution of new stored computed fields.
3043+
3044+
:param: field_spec: List of tuples with the following expected elements
3045+
for each tuple:
3046+
3047+
* model name
3048+
* field name
3049+
* field type: binary, boolean, char, date, datetime, float, html,
3050+
integer, many2many, many2one, many2one_reference, monetary, one2many,
3051+
reference, selection, text, serialized. The list can vary depending on
3052+
Odoo version or custom added field types.
3053+
* (optional) initialization value: if included in the tuple, it is set
3054+
in the column for existing records.
3055+
* (optional) SQL table name
3056+
* (optional) SQL field type: If the field type is custom or if it's one
3057+
of the special cases (see get_field2column_type), you need to indicate
3058+
here the SQL type to use (from the valid PostgreSQL types):
3059+
https://www.postgresql.org/docs/9.6/static/datatype.html
3060+
* (optional) translatable: From >=v16, if field is translatable then
3061+
SQL field type is changed to 'jsonb'.
3062+
"""
3063+
cr = env.cr
3064+
for vals in field_spec:
3065+
model_name = vals[0]
3066+
field_name = vals[1]
3067+
field_type = vals[2]
3068+
init_value = vals[3] if len(vals) > 3 else False
3069+
table_name = vals[4] if len(vals) > 4 else False
3070+
sql_type = vals[5] if len(vals) > 5 else False
3071+
translatable = vals[6] if len(vals) > 6 else False
3072+
sql_type = sql_type or get_field2column_type(field_type, translatable)
3073+
if version_info[0] > 15 and translatable:
3074+
from psycopg2.extras import Json
3075+
3076+
init_value = init_value and Json({"en_US": init_value})
3077+
if not table_name:
3078+
try:
3079+
table_name = env[model_name]._table
3080+
except KeyError:
3081+
table_name = get_model2table(model_name)
3082+
if sql_type and not column_exists(cr, table_name, field_name):
3083+
query = sql.SQL("ALTER TABLE {} ADD COLUMN {} {}").format(
3084+
sql.Identifier(table_name),
3085+
sql.Identifier(field_name),
3086+
sql.SQL(sql_type),
3087+
)
3088+
args = []
3089+
if init_value:
3090+
query += sql.SQL(" DEFAULT %s")
3091+
args.append(init_value)
3092+
logged_query(cr, query, args)
3093+
if init_value:
3094+
logged_query(
3095+
cr,
3096+
sql.SQL("ALTER TABLE {} ALTER COLUMN {} DROP DEFAULT").format(
3097+
sql.Identifier(table_name),
3098+
sql.Identifier(field_name),
3099+
),
3100+
)
3101+
3102+
29983103
def add_fields(env, field_spec):
29993104
"""This method adds all the needed stuff for having a new field populated
30003105
in the DB (SQL column, ir.model.fields entry, ir.model.data entry...).
@@ -3006,7 +3111,7 @@ def add_fields(env, field_spec):
30063111
always add the XML-ID entry:
30073112
https://github.com/odoo/odoo/blob/9201f92a4f29a53a014b462469f27b32dca8fc5a/
30083113
odoo/addons/base/models/ir_model.py#L794-L802, but you can still call
3009-
this method for consistency and for avoiding to know the internal PG
3114+
this method for consistency and for avoiding knowing the internal PG
30103115
column type.
30113116
30123117
:param: field_spec: List of tuples with the following expected elements
@@ -3020,33 +3125,18 @@ def add_fields(env, field_spec):
30203125
integer, many2many, many2one, many2one_reference, monetary, one2many,
30213126
reference, selection, text, serialized. The list can vary depending on
30223127
Odoo version or custom added field types.
3023-
* SQL field type: If the field type is custom or it's one of the special
3024-
cases (see below), you need to indicate here the SQL type to use
3025-
(from the valid PostgreSQL types):
3128+
* SQL field type: If the field type is custom or if it's one of the special
3129+
cases (see get_field2column_type), you need to indicate here the SQL type
3130+
to use (from the valid PostgreSQL types):
30263131
https://www.postgresql.org/docs/9.6/static/datatype.html
3132+
3133+
Note: From >=v16, if field is translatable, SQL field type has to be
3134+
explicitly stated as 'jsonb'.
3135+
30273136
* module name: for adding the XML-ID entry.
30283137
* (optional) initialization value: if included in the tuple, it is set
30293138
in the column for existing records.
30303139
"""
3031-
sql_type_mapping = {
3032-
"binary": "bytea", # If there's attachment, no SQL. Force it manually
3033-
"boolean": "bool",
3034-
"char": "varchar", # Force it manually if there's size limit
3035-
"date": "date",
3036-
"datetime": "timestamp",
3037-
"float": "numeric", # Force manually to double precision if no digits
3038-
"html": "text",
3039-
"integer": "int4",
3040-
"many2many": False, # No need to create SQL column
3041-
"many2one": "int4",
3042-
"many2one_reference": "int4",
3043-
"monetary": "numeric",
3044-
"one2many": False, # No need to create SQL column
3045-
"reference": "varchar",
3046-
"selection": "varchar", # Can be sometimes integer. Force it manually
3047-
"text": "text",
3048-
"serialized": "text",
3049-
}
30503140
for vals in field_spec:
30513141
field_name = vals[0]
30523142
model_name = vals[1]
@@ -3055,33 +3145,16 @@ def add_fields(env, field_spec):
30553145
sql_type = vals[4]
30563146
module = vals[5]
30573147
init_value = vals[6] if len(vals) > 6 else False
3058-
# Add SQL column
30593148
if not table_name:
30603149
try:
30613150
table_name = env[model_name]._table
30623151
except KeyError:
30633152
table_name = get_model2table(model_name)
3064-
if not column_exists(env.cr, table_name, field_name):
3065-
sql_type = sql_type or sql_type_mapping.get(field_type)
3066-
if sql_type:
3067-
query = sql.SQL("ALTER TABLE {} ADD COLUMN {} {}").format(
3068-
sql.Identifier(table_name),
3069-
sql.Identifier(field_name),
3070-
sql.SQL(sql_type),
3071-
)
3072-
args = []
3073-
if init_value:
3074-
query += sql.SQL(" DEFAULT %s")
3075-
args.append(init_value)
3076-
logged_query(env.cr, query, args)
3077-
if init_value:
3078-
logged_query(
3079-
env.cr,
3080-
sql.SQL("ALTER TABLE {} ALTER COLUMN {} DROP DEFAULT").format(
3081-
sql.Identifier(table_name),
3082-
sql.Identifier(field_name),
3083-
),
3084-
)
3153+
# Add SQL column
3154+
add_columns(
3155+
env,
3156+
[(model_name, field_name, field_type, init_value, table_name, sql_type)],
3157+
)
30853158
# Add ir.model.fields entry
30863159
env.cr.execute(
30873160
"SELECT id FROM ir_model WHERE model = %s",

0 commit comments

Comments
 (0)