diff --git a/unserialize_field/__init__.py b/unserialize_field/__init__.py new file mode 100644 index 000000000..81f4e3698 --- /dev/null +++ b/unserialize_field/__init__.py @@ -0,0 +1,22 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV () +# All Rights Reserved +# +# 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 . +# +############################################################################## +import ir_model_fields diff --git a/unserialize_field/__openerp__.py b/unserialize_field/__openerp__.py new file mode 100644 index 000000000..b639ce348 --- /dev/null +++ b/unserialize_field/__openerp__.py @@ -0,0 +1,46 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV () +# All Rights Reserved +# +# 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 . +# +############################################################################## + +{ + 'name': 'Make database fields from fields that live in serialized fields', + 'version': '1.0', + 'description': """ +Sparse, or serialized fields do not live as a column in the database table. +Instead, their values are stored in JSON arrays in a separate field. As a +result, these fields cannot be searched or sorted by. + +If such a sparse field is created as 'manual', this module can unserialize +the field. Its field definition and values will then be migrated to a +proper database column. A real life use case where you encounter many +of such fields is the Magento-OpenERP connector. + +For technical reasons, many2many and one2many fields are not supported. +""", + 'author': 'Therp BV', + 'website': 'http://www.therp.nl', + 'version': '1.0', + "category": "Tools", + "depends": ['base'], + "data": ['ir_model_fields.xml'], + 'installable': True, + 'active': False, +} diff --git a/unserialize_field/ir_model_fields.py b/unserialize_field/ir_model_fields.py new file mode 100644 index 000000000..c5b5699f3 --- /dev/null +++ b/unserialize_field/ir_model_fields.py @@ -0,0 +1,115 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV () +# All Rights Reserved +# +# 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 . +# +############################################################################## +from openerp.osv import orm +from openerp.tools.translate import _ + + +class ir_model_fields(orm.Model): + _inherit = 'ir.model.fields' + + def action_unserialize_field(self, cr, uid, ids, context=None): + step = 1000 + offset = 0 + + # Prevent _auto_init to commit the transaction + # before the data is migrated safely + commit_org = cr.commit + cr.commit = lambda *args: None + + try: + for this in self.browse(cr, uid, ids, context=context): + pool_obj = self.pool.get(this.model_id.model) + self.create_database_column(cr, uid, pool_obj, this.name, + context=context) + while True: + ids = pool_obj.search( + cr, uid, + [(this.serialization_field_id.name, '!=', '{}')], + offset=offset*step, limit=step, context=context) + if not ids: + break + for data in pool_obj.read(cr, uid, ids, + [this.serialization_field_id.name], + context=context): + self.unserialize_field(cr, uid, pool_obj, data, + this.serialization_field_id.name, + this.name, context=context) + offset += 1 + finally: + cr.commit = commit_org + + return True + + def create_database_column(self, cr, uid, pool_obj, field_name, + context=None): + old = pool_obj._columns[field_name] + if not old.manual: + raise orm.except_orm( + _('Error'), + _('This operation can only be performed on manual fields')) + if old._type == 'many2many': + # Cross table name length of manually created many2many + # fields can easily become too large. Although it would + # probably work if the table name length was within bounds, + # this scenario has not been tested because of this limitation. + raise orm.except_orm( + _("Error"), + _("Many2many fields are not supported. See " + "https://bugs.launchpad.net/openobject-server/+bug/1174078 " + "for more information")) + if old._type == 'one2many': + # How to get a safe field name for the relation field + # on the target model? + raise orm.except_orm( + _("Error"), + _("One2many fields are not handled yet")) + + # ORM prohibits to change the 'storing system' of the field + cr.execute(""" + UPDATE ir_model_fields + SET serialization_field_id = NULL + WHERE name = %s and model = %s + """, (field_name, pool_obj._name)) + + del pool_obj._columns[field_name] + pool_obj.__init__(self.pool, cr) + pool_obj._auto_init(cr, {'update_custom_fields': True}) + + def unserialize_field(self, cr, uid, pool_obj, read_record, + serialization_field_name, field_name, + context=None): + serialized_values = read_record[serialization_field_name] + if not field_name in serialized_values: + return False + + value = serialized_values.pop(field_name) + if pool_obj._columns[field_name]._type in ('many2many', 'one2many'): + value = [(6, 0, value)] + + return pool_obj.write( + cr, uid, read_record['id'], + { + field_name: value, + serialization_field_name: serialized_values, + }, + context=context) + diff --git a/unserialize_field/ir_model_fields.xml b/unserialize_field/ir_model_fields.xml new file mode 100644 index 000000000..4a92c4d39 --- /dev/null +++ b/unserialize_field/ir_model_fields.xml @@ -0,0 +1,36 @@ + + + + + form + ir.model.fields + + + + +