[IMP] move conversion out of view.save

Also, html.fromstring does weird stuff when encoding is not specified,
force to utf-8 just in case.

bzr revid: xmo@openerp.com-20130930145358-qh7xdicgg21prsk4
This commit is contained in:
Xavier Morel 2013-09-30 16:53:58 +02:00
parent 3b6f61b2ac
commit ce30e7691e
8 changed files with 224 additions and 51 deletions

View File

@ -1,5 +1,3 @@
import controllers
import res_config
import website
import view
import models

View File

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
import ir_fields
import res_config
import view
import website

View File

@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
from lxml import etree, html
from openerp.osv import orm, fields
from openerp.tools import ustr
class converter(orm.Model):
_inherit = 'ir.fields.converter'
def _html_to_integer(self, cr, uid, model, column, value, context=None):
return int(value.text_content().strip()), []
def _html_to_float(self, cr, uid, model, column, value, context=None):
return float(value.text_content().strip()), []
def _html_to_passthrough(self, cr, uid, model, column, value, context=None):
return value.text_content().strip(), []
_html_to_char = _html_to_date = _html_to_datetime = _html_to_passthrough
def _html_to_text(self, cr, uid, model, column, value, context=None):
return value.text_content(), []
def _html_to_selection(self, cr, uid, model, column, value, context=None):
text = value.text_content().strip()
selection = column.reify(cr, uid, model, column, context=context)
for k, v in selection:
if isinstance(v, str):
v = ustr(v)
if text == v:
return k, []
warning = u"No value found for label %s in selection %s" % (text, selection)
# FIXME: ?
return False, [Warning(warning.encode('utf-8'))]
def _html_to_many2one(self, cr, uid, model, column, value, context=None):
matches = self.pool[column._obj].name_search(
cr, uid, name=value.text_content().strip(), context=context)
# FIXME: more than one match, error reporting
return matches[0][0], []
def _html_to_html(self, cr, uid, model, column, value, context=None):
content = []
if value.text: content.append(value.text)
content.extend(html.tostring(child)
for child in value.iterchildren(tag=etree.Element))
return '\n'.join(content), []
class test_converter(orm.Model):
_name = 'website.converter.test'
_columns = {
'char': fields.char(),
'integer': fields.integer(),
'float': fields.float(),
'numeric': fields.float(digits=(16, 2)),
'many2one': fields.many2one('website.converter.test.sub'),
'binary': fields.binary(),
'date': fields.date(),
'datetime': fields.datetime(),
'selection': fields.selection([
(1, "réponse A"),
(2, "réponse B"),
(3, "réponse C"),
(4, "réponse D"),
]),
'selection_str': fields.selection([
('A', "Qu'il n'est pas arrivé à Toronto"),
('B', "Qu'il était supposé arriver à Toronto"),
('C', "Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?"),
('D', "La réponse D"),
], string="Lorsqu'un pancake prend l'avion à destination de Toronto et "
"qu'il fait une escale technique à St Claude, on dit:"),
'html': fields.html(),
'text': fields.text(),
}
class test_converter_sub(orm.Model):
_name = 'website.converter.test.sub'
_columns = {
'name': fields.char(),
}

View File

@ -55,57 +55,19 @@ class view(osv.osv):
def extract_embedded_fields(self, cr, uid, arch, context=None):
return arch.xpath('//*[@data-oe-model != "ir.ui.view"]')
def convert_embedded_field(self, cr, uid, el, column,
type_override=None, context=None):
""" Converts the content of an embedded field to a value acceptable
for writing in the column
:param etree._Element el: embedded field being saved
:param fields._column column: column object corresponding to the field
:param type type_override: column type to dispatch on instead of the
column's actual type (for proxy column types
e.g. relateds)
:return: converted value
"""
column_type = type_override or type(column)
if issubclass(column_type, fields.html):
content = []
if el.text: content.append(el.text)
content.extend(html.tostring(child)
for child in el.iterchildren(tag=etree.Element))
return '\n'.join(content)
elif issubclass(column_type, fields.integer):
return int(el.text_content())
elif issubclass(column_type, fields.float):
return float(el.text_content())
elif issubclass(column_type, (fields.char, fields.text,
fields.date, fields.datetime)):
return el.text_content()
# TODO: fields.selection
elif issubclass(column_type, fields.many2one):
matches = self.pool[column._obj].name_search(
cr, uid, name=el.text_content().strip(), context=context)
# FIXME: more than one match, error reporting
return matches[0][0]
elif issubclass(column_type, fields.function):
# FIXME: special-case selection as in get_pg_type?
return self.convert_embedded_field(
cr, uid, el, column,
type_override=getattr(fields, column._type),
context=context)
# TODO?: fields.many2many, fields.one2many
# TODO?: fields.reference
else:
raise TypeError("Un-convertable column type %s" % column_type)
def save_embedded_field(self, cr, uid, el, context=None):
Model = self.pool[el.get('data-oe-model')]
field = el.get('data-oe-field')
column = Model._all_columns[field].column
convert = self.pool['ir.fields.converter'].to_field(
cr, uid, Model, column, fromtype="html", context=context)
value, warnings = convert(el)
# FIXME: report error
if warnings: return
Model.write(cr, uid, [int(el.get('data-oe-id'))], {
field: self.convert_embedded_field(cr, uid, el, column, context=context)
field: value
}, context=context)
def to_field_ref(self, cr, uid, el, context=None):
@ -137,7 +99,8 @@ class view(osv.osv):
"""
res_id = int(res_id)
arch_section = html.fromstring(value)
arch_section = html.fromstring(
value, parser=html.HTMLParser(encoding='utf-8'))
if xpath is None:
# value is an embedded field on its own, not a view section

View File

@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
import test_views
import test_views, test_converter
checks = [ test_views ]
checks = [ test_views, test_converter, ]

View File

@ -0,0 +1,119 @@
# -*- coding: utf-8 -*-
from collections import namedtuple
from functools import partial
from xml.dom.minidom import getDOMImplementation
from lxml import etree, html
from openerp.tests import common
from openerp.tools import qweb
import unittest2
renderer = qweb.QWebXml()
impl = getDOMImplementation()
document = impl.createDocument(None, None, None)
Request = namedtuple('Request', 'cr uid registry')
class RegistryProxy(object):
def __init__(self, func):
self.func = func
def __getitem__(self, name):
return self.func(name)
class TestConvertBack(common.TransactionCase):
def setUp(self):
super(TestConvertBack, self).setUp()
self.Converter = self.registry('ir.fields.converter')
def field_rountrip_result(self, field, value, expected):
model = 'website.converter.test'
Model = self.registry(model)
id = Model.create(
self.cr, self.uid, {
field: value
})
[record] = Model.browse(self.cr, self.uid, [id])
e = document.createElement('span')
field_value = 'record.%s' % field
e.setAttribute('t-field', field_value)
rendered = renderer.render_tag_field(
e, {'field': field_value}, '', {
'record': record,
'request': Request(self.cr, self.uid, RegistryProxy( self.registry)),
})
element = html.fromstring(
rendered, parser=html.HTMLParser(encoding='utf-8'))
column = Model._all_columns[field].column
from_html = self.Converter.to_field(
self.cr, self.uid, model, column, 'html')
value_back, warnings = from_html(element)
self.assertEqual(warnings, [])
if isinstance(expected, str):
expected = expected.decode('utf-8')
self.assertEqual(value_back, expected)
def field_roundtrip(self, field, value):
self.field_rountrip_result(field, value, value)
def test_integer(self):
self.field_roundtrip('integer', 42)
def test_float(self):
self.field_roundtrip('float', 42.567890)
def test_numeric(self):
self.field_roundtrip('numeric', 42.77)
def test_char(self):
self.field_roundtrip('char', "foo bar")
self.field_roundtrip('char', "ⒸⓄⓇⒼⒺ")
def test_m2o(self):
Sub = self.registry('website.converter.test.sub')
sub = partial(Sub.create, self.cr, self.uid)
ids = [
sub({'name': "Foo"}),
sub({'name': "Bar"}),
sub({'name': "Baz"}),
]
self.field_rountrip_result(
'many2one',
ids[2],
ids[2])
def test_selection(self):
self.field_roundtrip('selection', 3)
def test_selection_str(self):
self.field_roundtrip('selection_str', 'B')
def test_text(self):
self.field_roundtrip('text', """
You must obey the dance commander
Givin' out the order for fun
You must obey the dance commander
You know that he's the only one
Who gives the orders here,
Alright
Who gives the orders here,
Alright
It would be awesome
If we could dance-a
It would be awesome, yeah
Let's take the chance-a
It would be awesome, yeah
Let's start the show
Because you never know
You never know
You never know until you go
""")