David BEAL has proposed merging lp:~akretion-team/openerp-product-attributes/openerp-product-attributes_limit_database_column_name into lp:openerp-product-attributes.
Requested reviews: Benoit Guillot - http://www.akretion.com (benoit-guillot-z) Guewen Baconnier @ Camptocamp (gbaconnier-c2c) Quentin THEURET @TeMPO Consulting (qt-tempo-consulting): code review Sébastien BEAU - http://www.akretion.com (sebastien.beau) For more details, see: https://code.launchpad.net/~akretion-team/openerp-product-attributes/openerp-product-attributes_limit_database_column_name/+merge/194998 [IMP] add a function to define allowed chars for database column name + cleaning code .py alignement -- https://code.launchpad.net/~akretion-team/openerp-product-attributes/openerp-product-attributes_limit_database_column_name/+merge/194998 Your team OpenERP Community is subscribed to branch lp:openerp-product-attributes.
=== modified file 'base_custom_attributes/custom_attributes.py' --- base_custom_attributes/custom_attributes.py 2013-11-12 08:30:11 +0000 +++ base_custom_attributes/custom_attributes.py 2013-11-13 08:15:07 +0000 @@ -1,9 +1,9 @@ # -*- encoding: utf-8 -*- ############################################################################### # # -# base_attribute.attributes for OpenERP # -# Copyright (C) 2011 Akretion Benoît GUILLOT <[email protected]> # -# Copyright (C) 2013 Akretion Raphaël VALYI <[email protected]> # +# base_attribute.attributes for OpenERP # +# Copyright (C) 2011 Akretion Benoît GUILLOT <[email protected]> +# Copyright (C) 2013 Akretion Raphaël VALYI <[email protected]> # # # This program is free software: you can redistribute it and/or modify # # it under the terms of the GNU Affero General Public License as # @@ -26,6 +26,15 @@ from openerp.tools.translate import _ from lxml import etree from unidecode import unidecode # Debian package python-unidecode +import re + + +def safe_column_name(string): + """This function prevent portability problem in database column name + with other DBMS system + Use case : if you synchronise attributes with other applications """ + string = unidecode(string.replace(' ', '_').lower()) + return re.sub(r'[^0-9a-z_]','', string) class attribute_option(orm.Model): @@ -40,15 +49,38 @@ return [(r['model'], r['name']) for r in res] _columns = { +<<<<<<< TREE 'name': fields.char('Name', size=128, translate=True, required=True), 'value_ref': fields.reference('Reference', selection=_get_model_list, size=128), 'attribute_id': fields.many2one('attribute.attribute', 'Product Attribute', required=True), +======= + 'name': fields.char( + 'Name', + size=128, + translate=True, + required=True), + 'value_ref': fields.reference( + 'Reference', + selection=[], + size=128), + 'attribute_id': fields.many2one( + 'attribute.attribute', + 'Product Attribute', + required=True), +>>>>>>> MERGE-SOURCE 'sequence': fields.integer('Sequence'), } def name_change(self, cr, uid, ids, name, relation_model_id, context=None): if relation_model_id: +<<<<<<< TREE warning = {'title': _('Error!'), 'message': _("Use the 'Load Options' button instead to select appropriate model references.")} +======= + warning = {'title': _('Error!'), + 'message': _("Use the 'Change Options' button " + "instead to select appropriate " + "model references'")} +>>>>>>> MERGE-SOURCE return {"value": {"name": False}, "warning": warning} else: return True @@ -59,11 +91,19 @@ _rec_name = 'attribute_id' _columns = { +<<<<<<< TREE 'attribute_id': fields.many2one('attribute.attribute', 'Attribute', required=True), +======= + 'attribute_id': fields.many2one( + 'attribute.attribute', + 'Product Attribute', + required=True), +>>>>>>> MERGE-SOURCE } _defaults = { - 'attribute_id': lambda self, cr, uid, context: context.get('attribute_id', False) + 'attribute_id': lambda self, cr, uid, context: + context.get('attribute_id',False) } def validate(self, cr, uid, ids, context=None): @@ -86,12 +126,16 @@ res = super(attribute_option_wizard, self).create(cr, uid, vals, context) return res - def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): - res = super(attribute_option_wizard, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu) + def fields_view_get(self, cr, uid, view_id=None, view_type='form', + context=None, toolbar=False, submenu=False): + res = super(attribute_option_wizard, self).fields_view_get( + cr, uid, view_id, view_type, context, toolbar, submenu) if view_type == 'form' and context and context.get("attribute_id"): attr_obj = self.pool.get("attribute.attribute") - model_id = attr_obj.read(cr, uid, [context.get("attribute_id")], ['relation_model_id'])[0]['relation_model_id'][0] - relation = self.pool.get("ir.model").read(cr, uid, [model_id], ["model"])[0]["model"] + model_id = attr_obj.read(cr, uid, [context.get("attribute_id")], + ['relation_model_id'])[0]['relation_model_id'][0] + relation = self.pool.get("ir.model").read(cr, uid, [model_id], + ["model"])[0]["model"] res['fields'].update({'option_ids': { 'domain': [], 'string': "Options", @@ -118,11 +162,11 @@ kwargs = {'name': "%s" % attribute.name} if attribute.ttype in ['many2many', 'text']: parent = etree.SubElement(parent, 'group', colspan="2", col="4") + #seems to be unused sep = etree.SubElement(parent, 'separator', string="%s" % attribute.field_description, - colspan="4") - + colspan="4") kwargs['nolabel'] = "1" if attribute.ttype in ['many2one', 'many2many']: if attribute.relation_model_id: @@ -142,23 +186,30 @@ kwargs['required'] = str(attribute.required or attribute.required_on_views) field = etree.SubElement(parent, 'field', **kwargs) - orm.setup_modifiers(field, self.fields_get(cr, uid, attribute.name, context)) + orm.setup_modifiers(field, self.fields_get(cr, uid, attribute.name, + context)) return parent - def _build_attributes_notebook(self, cr, uid, attribute_group_ids, context=None): - notebook = etree.Element('notebook', name="attributes_notebook", colspan="4") + def _build_attributes_notebook(self, cr, uid, attribute_group_ids, + context=None): + notebook = etree.Element('notebook', name="attributes_notebook", + colspan="4") toupdate_fields = [] grp_obj = self.pool.get('attribute.group') - for group in grp_obj.browse(cr, uid, attribute_group_ids, context=context): - page = etree.SubElement(notebook, 'page', string=group.name.capitalize()) + for group in grp_obj.browse(cr, uid, attribute_group_ids, + context=context): + page = etree.SubElement(notebook, 'page', + string=group.name.capitalize()) for attribute in group.attribute_ids: if attribute.name not in toupdate_fields: toupdate_fields.append(attribute.name) - self._build_attribute_field(cr, uid, page, attribute, context=context) + self._build_attribute_field(cr, uid, page, attribute, + context=context) return notebook, toupdate_fields - def relation_model_id_change(self, cr, uid, ids, relation_model_id, option_ids, context=None): - "removed selected options as they would be inconsistent" + def relation_model_id_change(self, cr, uid, ids, relation_model_id, + option_ids, context=None): + "removed selected options as they would be inconsistent" return {'value': {'option_ids': [(2, i[1]) for i in option_ids]}} def button_add_options(self, cr, uid, ids, context=None): @@ -173,24 +224,36 @@ } _columns = { - 'field_id': fields.many2one('ir.model.fields', 'Ir Model Fields', required=True, ondelete="cascade"), - 'attribute_type': fields.selection([('char','Char'), - ('text','Text'), - ('select','Select'), - ('multiselect','Multiselect'), - ('boolean','Boolean'), - ('integer','Integer'), - ('date','Date'), - ('datetime','Datetime'), - ('binary','Binary'), - ('float','Float')], - 'Type', required=True), - 'serialized': fields.boolean('Field serialized', - help="If serialized, the field will be stocked in the serialized field: " - "attribute_custom_tmpl or attribute_custom_variant depending on the field based_on"), - 'option_ids': fields.one2many('attribute.option', 'attribute_id', 'Attribute Options'), + 'field_id': fields.many2one( + 'ir.model.fields', + 'Ir Model Fields', + required=True, + ondelete="cascade"), + 'attribute_type': fields.selection([ + ('char', 'Char'), + ('text', 'Text'), + ('select', 'Select'), + ('multiselect', 'Multiselect'), + ('boolean', 'Boolean'), + ('integer', 'Integer'), + ('date', 'Date'), + ('datetime', 'Datetime'), + ('binary', 'Binary'), + ('float', 'Float')], + 'Type', required=True), + 'serialized': fields.boolean( + 'Field serialized', + help="If serialized, the field will be stocked in the serialized " + "field: attribute_custom_tmpl or attribute_custom_variant " + "depending on the field based_on"), + 'option_ids': fields.one2many( + 'attribute.option', + 'attribute_id', + 'Attribute Options'), 'create_date': fields.datetime('Created date', readonly=True), - 'relation_model_id': fields.many2one('ir.model', 'Model'), + 'relation_model_id': fields.many2one( + 'ir.model', + 'Model'), 'required_on_views': fields.boolean( 'Required (on views)', help="If activated, the attribute will be mandatory on the views, " @@ -199,11 +262,10 @@ def create(self, cr, uid, vals, context=None): if vals.get('relation_model_id'): - relation = self.pool.get('ir.model').read(cr, uid, - [vals.get('relation_model_id')], ['model'])[0]['model'] + relation = self.pool.get('ir.model').read( + cr, uid, [vals.get('relation_model_id')], ['model'])[0]['model'] else: relation = 'attribute.option' - if vals['attribute_type'] == 'select': vals['ttype'] = 'many2one' vals['relation'] = relation @@ -216,27 +278,31 @@ if vals.get('serialized'): field_obj = self.pool.get('ir.model.fields') - serialized_ids = field_obj.search(cr, uid, - [('ttype', '=', 'serialized'), ('model_id', '=', vals['model_id']), - ('name', '=', 'x_custom_json_attrs')], context=context) + serialized_ids = field_obj.search(cr, uid, [ + ('ttype', '=', 'serialized'), + ('model_id', '=', vals['model_id']), + ('name', '=', 'x_custom_json_attrs')], + context=context) if serialized_ids: vals['serialization_field_id'] = serialized_ids[0] else: f_vals = { 'name': u'x_custom_json_attrs', - 'field_description': u'Serialized JSON Attributes', + 'field_description': u'Serialized JSON Attributes', 'ttype': 'serialized', 'model_id': vals['model_id'], } - vals['serialization_field_id'] = field_obj.create(cr, uid, f_vals, {'manual': True}) + vals['serialization_field_id'] = field_obj.create( + cr, uid, f_vals, {'manual': True}) vals['state'] = 'manual' return super(attribute_attribute, self).create(cr, uid, vals, context) - def onchange_field_description(self, cr, uid, ids, field_description, name, create_date, context=None): + def onchange_field_description(self, cr, uid, ids, field_description, + name, create_date, context=None): name = name or u'x_' if field_description and not create_date: - name = unidecode(u'x_%s' % field_description.replace(' ', '_').lower()) - return {'value' : {'name' : name}} + name = unicode('x_' + safe_column_name(field_description)) + return {'value': {'name': name}} def onchange_name(self, cr, uid, ids, name, context=None): res = {} @@ -244,15 +310,15 @@ name = u'x_%s' % name else: name = u'%s' % name - res = {'value' : {'name' : unidecode(name)}} + res = {'value': {'name': unidecode(name)}} #FILTER ON MODEL - model_domain = [] model_name = context.get('force_model') if not model_name: model_id = context.get('default_model_id') if model_id: - model = self.pool['ir.model'].browse(cr, uid, model_id, context=context) + model = self.pool['ir.model'].browse(cr, uid, model_id, + context=context) model_name = model.model if model_name: model_obj = self.pool[model_name] @@ -264,8 +330,8 @@ def _get_default_model(self, cr, uid, context=None): if context and context.get('force_model'): model_id = self.pool['ir.model'].search(cr, uid, [ - ['model', '=', context['force_model']] - ], context=context) + ('model', '=', context['force_model']) + ], context=context) if model_id: return model_id[0] return None @@ -276,29 +342,45 @@ class attribute_group(orm.Model): - _name= "attribute.group" + _name = "attribute.group" _description = "Attribute Group" - _order="sequence" + _order ="sequence" _columns = { +<<<<<<< TREE 'name': fields.char('Name', size=128, required=True, translate=True), +======= + 'name': fields.char( + 'Name', + size=128, + required=True), +>>>>>>> MERGE-SOURCE 'sequence': fields.integer('Sequence'), - 'attribute_set_id': fields.many2one('attribute.set', 'Attribute Set'), - 'attribute_ids': fields.one2many('attribute.location', 'attribute_group_id', 'Attributes'), - 'model_id': fields.many2one('ir.model', 'Model', required=True), + 'attribute_set_id': fields.many2one( + 'attribute.set', + 'Attribute Set'), + 'attribute_ids': fields.one2many( + 'attribute.location', + 'attribute_group_id', + 'Attributes'), + 'model_id': fields.many2one( + 'ir.model', + 'Model', + required=True), } def create(self, cr, uid, vals, context=None): - for attribute in vals.get('attribute_ids', []): - if vals.get('attribute_set_id') and attribute[2] and not attribute[2].get('attribute_set_id'): + for attribute in vals['attribute_ids']: + if vals.get('attribute_set_id') and attribute[2] and \ + not attribute[2].get('attribute_set_id'): attribute[2]['attribute_set_id'] = vals['attribute_set_id'] return super(attribute_group, self).create(cr, uid, vals, context) def _get_default_model(self, cr, uid, context=None): if context and context.get('force_model'): - model_id = self.pool['ir.model'].search(cr, uid, [ - ['model', '=', context['force_model']] - ], context=context) + model_id = self.pool['ir.model'].search( + cr, uid, [['model', '=', context['force_model']]], + context=context) if model_id: return model_id[0] return None @@ -312,16 +394,31 @@ _name = "attribute.set" _description = "Attribute Set" _columns = { +<<<<<<< TREE 'name': fields.char('Name', size=128, required=True, translate=True), 'attribute_group_ids': fields.one2many('attribute.group', 'attribute_set_id', 'Attribute Groups'), 'model_id': fields.many2one('ir.model', 'Model', required=True), +======= + 'name': fields.char( + 'Name', + size=128, + required=True), + 'attribute_group_ids': fields.one2many( + 'attribute.group', + 'attribute_set_id', + 'Attribute Groups'), + 'model_id': fields.many2one( + 'ir.model', + 'Model', + required=True), +>>>>>>> MERGE-SOURCE } def _get_default_model(self, cr, uid, context=None): if context and context.get('force_model'): - model_id = self.pool['ir.model'].search(cr, uid, [ - ['model', '=', context['force_model']] - ], context=context) + model_id = self.pool['ir.model'].search( + cr, uid, [['model', '=', context['force_model']]], + context=context) if model_id: return model_id[0] return None @@ -330,22 +427,46 @@ 'model_id': _get_default_model } + class attribute_location(orm.Model): _name = "attribute.location" _description = "Attribute Location" _order="sequence" _inherits = {'attribute.attribute': 'attribute_id'} - def _get_attribute_loc_from_group(self, cr, uid, ids, context=None): - return self.pool.get('attribute.location').search(cr, uid, [('attribute_group_id', 'in', ids)], context=context) + return self.pool.get('attribute.location').search( + cr, uid, [('attribute_group_id', 'in', ids)], context=context) _columns = { +<<<<<<< TREE 'attribute_id': fields.many2one('attribute.attribute', 'Attribute', required=True, ondelete="cascade"), 'attribute_set_id': fields.related('attribute_group_id', 'attribute_set_id', type='many2one', relation='attribute.set', string='Attribute Set', readonly=True, store={ 'attribute.group': (_get_attribute_loc_from_group, ['attribute_set_id'], 10), }), 'attribute_group_id': fields.many2one('attribute.group', 'Attribute Group', required=True), +======= + 'attribute_id': fields.many2one( + 'attribute.attribute', + 'Product Attribute', + required=True, + ondelete="cascade"), + 'attribute_set_id': fields.related( + 'attribute_group_id', + 'attribute_set_id', + type='many2one', + relation='attribute.set', + string='Attribute Set', + readonly=True, + store={ + 'attribute.group': (_get_attribute_loc_from_group, + ['attribute_set_id'], 10), + }), + 'attribute_group_id': fields.many2one( + 'attribute.group', + 'Attribute Group', + required=True), +>>>>>>> MERGE-SOURCE 'sequence': fields.integer('Sequence'), } === modified file 'base_custom_attributes/ir_model.py' --- base_custom_attributes/ir_model.py 2012-08-21 14:10:21 +0000 +++ base_custom_attributes/ir_model.py 2013-11-13 08:15:07 +0000 @@ -1,8 +1,8 @@ # -*- encoding: utf-8 -*- ############################################################################### # # -# product_custom_attributes for OpenERP # -# Copyright (C) 2011 Akretion Benoît GUILLOT <[email protected]> # +# product_custom_attributes for OpenERP +# Copyright (C) 2011 Akretion Benoît GUILLOT <[email protected]> # # # This program is free software: you can redistribute it and/or modify # # it under the terms of the GNU Affero General Public License as # @@ -27,8 +27,13 @@ _inherit = "ir.model.fields" _columns = { - 'field_description': fields.char('Field Label', required=True, size=256, translate=True), + 'field_description': fields.char( + 'Field Label', + required=True, + size=256, + translate=True), } _sql_constraints = [ - ('name_model_uniq', 'unique (name, model_id)', 'The name of the field has to be uniq for a given model !'), + ('name_model_uniq', 'unique (name, model_id)', + 'The name of the field has to be uniq for a given model !'), ]
_______________________________________________ Mailing list: https://launchpad.net/~openerp-community Post to : [email protected] Unsubscribe : https://launchpad.net/~openerp-community More help : https://help.launchpad.net/ListHelp

