Files
odoo_source/odoo/addons/test_impex/models.py
T
Xavier Morel 7ac3a391a1 [FIX] core: don't break import on files triggering UnicodeEncodeError
The import logging (ish) assumes that if an exception has at least 2
args the second arg is metadata added by the callee.

As it turns out, `UnicodeEncodeError` has *five* arguments, none of
which is added by us. So if encoding something fails during the
process (e.g. because the file contains a lone surrogate, which leads
to the database insert failing when psycopg2 tries to encode the query
to UTF8), then the `_log` function itself will fail, yielding a very
unhelpful error of:

   dictionary update sequence element #0 has length 1; 2 is required

(because we tried to update a dict using a string).

This issue occurs only during *field conversion* and most fields have
no need to interact with the database (so don't need to encode the
value, which is what fails), however it is a problem when the invalid
string is used as a record name to look for (e.g. an m2o).

Further improve the experience by converting the UnicodeEncodeError to
a ValueError using the stringified UEE: `_log` assumes the first
argument to the exception is an error message of some sort, but for
UnicodeError subclasses it's just the encoding involved in the
error (here `utf-8`), which doesn't really serve as an error message.

Stringifying the exception generates a complete error message which is
quite a bit more helpful.

Specific update notes:

* avoid modifying the exception in-place, doesn't seem useful
* not sure why `field_name` was added as part of the augmentation
  rather than up-front when `record` is created

Issue 2480064

closes odoo/odoo#72517

X-original-commit: 6c3c500929cd463cd3a1749f4ede8f3a8afd5748
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2021-06-22 09:33:43 +00:00

187 lines
6.0 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
def selection_fn(model):
return [(str(key), val) for key, val in enumerate(["Corge", "Grault", "Wheee", "Moog"])]
def compute_fn(records):
for record in records:
record.value = 3
def inverse_fn(records):
pass
MODELS = [
('boolean', fields.Boolean()),
('integer', fields.Integer()),
('float', fields.Float()),
('decimal', fields.Float(digits=(16, 3))),
('string.bounded', fields.Char(size=16)),
('string.required', fields.Char(size=None, required=True)),
('string', fields.Char(size=None)),
('date', fields.Date()),
('datetime', fields.Datetime()),
('text', fields.Text()),
('selection', fields.Selection([('1', "Foo"), ('2', "Bar"), ('3', "Qux"), ('4', '')])),
('selection.function', fields.Selection(selection_fn)),
# just relate to an integer
('many2one', fields.Many2one('export.integer')),
('one2many', fields.One2many('export.one2many.child', 'parent_id')),
('many2many', fields.Many2many('export.many2many.other')),
('function', fields.Integer(compute=compute_fn, inverse=inverse_fn)),
# related: specialization of fields.function, should work the same way
# TODO: reference
]
for name, field in MODELS:
class NewModel(models.Model):
_name = 'export.%s' % name
_description = 'Export: %s' % name
_rec_name = 'value'
const = fields.Integer(default=4)
value = field
def name_get(self):
return [(record.id, "%s:%s" % (self._name, record.value)) for record in self]
@api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
if isinstance(name, str) and name.split(':')[0] == self._name:
return self._search([('value', operator, int(name.split(':')[1]))], access_rights_uid=name_get_uid)
else:
return []
class One2ManyChild(models.Model):
_name = 'export.one2many.child'
_description = 'Export One to Many Child'
# FIXME: orm.py:1161, fix to name_get on m2o field
_rec_name = 'value'
parent_id = fields.Many2one('export.one2many')
str = fields.Char()
m2o = fields.Many2one('export.integer')
value = fields.Integer()
def name_get(self):
return [(record.id, "%s:%s" % (self._name, record.value)) for record in self]
@api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
if isinstance(name, str) and name.split(':')[0] == self._name:
return self._search([('value', operator, int(name.split(':')[1]))], access_rights_uid=name_get_uid)
else:
return []
class One2ManyMultiple(models.Model):
_name = 'export.one2many.multiple'
_description = 'Export One To Many Multiple'
_rec_name = 'parent_id'
parent_id = fields.Many2one('export.one2many.recursive')
const = fields.Integer(default=36)
child1 = fields.One2many('export.one2many.child.1', 'parent_id')
child2 = fields.One2many('export.one2many.child.2', 'parent_id')
class One2ManyChildMultiple(models.Model):
_name = 'export.one2many.multiple.child'
# FIXME: orm.py:1161, fix to name_get on m2o field
_rec_name = 'value'
_description = 'Export One To Many Multiple Child'
parent_id = fields.Many2one('export.one2many.multiple')
str = fields.Char()
value = fields.Integer()
def name_get(self):
return [(record.id, "%s:%s" % (self._name, record.value)) for record in self]
class One2ManyChild1(models.Model):
_name = 'export.one2many.child.1'
_inherit = 'export.one2many.multiple.child'
_description = 'Export One to Many Child 1'
class One2ManyChild2(models.Model):
_name = 'export.one2many.child.2'
_inherit = 'export.one2many.multiple.child'
_description = 'Export One To Many Child 2'
class Many2ManyChild(models.Model):
_name = 'export.many2many.other'
_description = 'Export Many to Many Other'
# FIXME: orm.py:1161, fix to name_get on m2o field
_rec_name = 'value'
str = fields.Char()
value = fields.Integer()
def name_get(self):
return [(record.id, "%s:%s" % (self._name, record.value)) for record in self]
@api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
if isinstance(name, str) and name.split(':')[0] == self._name:
return self._search([('value', operator, int(name.split(':')[1]))], access_rights_uid=name_get_uid)
else:
return []
class SelectionWithDefault(models.Model):
_name = 'export.selection.withdefault'
_description = 'Export Selection With Default'
const = fields.Integer(default=4)
value = fields.Selection([('1', "Foo"), ('2', "Bar")], default='2')
class RecO2M(models.Model):
_name = 'export.one2many.recursive'
_description = 'Export One To Many Recursive'
_rec_name = 'value'
value = fields.Integer()
child = fields.One2many('export.one2many.multiple', 'parent_id')
class OnlyOne(models.Model):
_name = 'export.unique'
_description = 'Export Unique'
value = fields.Integer()
value2 = fields.Integer()
value3 = fields.Integer()
_sql_constraints = [
('value_unique', 'unique (value)', "The value must be unique"),
('pair_unique', 'unique (value2, value3)', "The values must be unique"),
]
class InheritsParent(models.Model):
_name = _description = 'export.inherits.parent'
value_parent = fields.Integer()
class InheritsChild(models.Model):
_name = _description = 'export.inherits.child'
_inherits = {'export.inherits.parent': 'parent_id'}
parent_id = fields.Many2one('export.inherits.parent', required=True, ondelete='cascade')
value = fields.Integer()
class Many2String(models.Model):
_name = _description = 'export.m2o.str'
child_id = fields.Many2one('export.m2o.str.child')
class ChidToString(models.Model):
_name = _description = 'export.m2o.str.child'
name = fields.Char()