openerp-community team mailing list archive
-
openerp-community team
-
Mailing list archive
-
Message #03971
lp:~akretion-team/openerp-product-attributes/openerp-product-attributes_limit_database_column_name into lp:openerp-product-attributes
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 <benoit.guillot@xxxxxxxxxxxx> #
-# Copyright (C) 2013 Akretion Raphaël VALYI <raphael.valyi@xxxxxxxxxxxx> #
+# base_attribute.attributes for OpenERP #
+# Copyright (C) 2011 Akretion Benoît GUILLOT <benoit.guillot@xxxxxxxxxxxx>
+# Copyright (C) 2013 Akretion Raphaël VALYI <raphael.valyi@xxxxxxxxxxxx>
# #
# 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 <benoit.guillot@xxxxxxxxxxxx> #
+# product_custom_attributes for OpenERP
+# Copyright (C) 2011 Akretion Benoît GUILLOT <benoit.guillot@xxxxxxxxxxxx>
# #
# 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 !'),
]
Follow ups