base_jsonify: support callable parser
parent
f947336f62
commit
e2e9bee9dc
|
@ -1,6 +1,8 @@
|
||||||
# © 2017 Akretion (http://www.akretion.com)
|
# Copyright 2017 Akretion (http://www.akretion.com)
|
||||||
# Sébastien BEAU <sebastien.beau@akretion.com>
|
# Sébastien BEAU <sebastien.beau@akretion.com>
|
||||||
# Raphaël Reverdy <raphael.reverdy@akretion.com>
|
# Raphaël Reverdy <raphael.reverdy@akretion.com>
|
||||||
|
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
|
||||||
|
# Simone Orsi <simahawk@gmail.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models
|
||||||
|
@ -33,6 +35,8 @@ class Base(models.AbstractModel):
|
||||||
'number',
|
'number',
|
||||||
'create_date',
|
'create_date',
|
||||||
('partner_id', ['id', 'display_name', 'ref'])
|
('partner_id', ['id', 'display_name', 'ref'])
|
||||||
|
('shipping_id', callable)
|
||||||
|
('delivery_id', "record_method")
|
||||||
('line_id', ['id', ('product_id', ['name']), 'price_unit'])
|
('line_id', ['id', ('product_id', ['name']), 'price_unit'])
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -64,6 +68,7 @@ class Base(models.AbstractModel):
|
||||||
def _jsonify_value(self, field_name):
|
def _jsonify_value(self, field_name):
|
||||||
field_type = self._fields[field_name].type
|
field_type = self._fields[field_name].type
|
||||||
value = self[field_name]
|
value = self[field_name]
|
||||||
|
# TODO: we should get default by field (eg: char field -> "")
|
||||||
if value is False and field_type != "boolean":
|
if value is False and field_type != "boolean":
|
||||||
value = None
|
value = None
|
||||||
elif field_type == "date":
|
elif field_type == "date":
|
||||||
|
@ -79,15 +84,26 @@ class Base(models.AbstractModel):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def _jsonify_value_subparser(self, field_name, subparser):
|
def _jsonify_value_subparser(self, field_name, subparser):
|
||||||
field_type = self._fields[field_name].type
|
|
||||||
if field_type in ("one2many", "many2many"):
|
|
||||||
value = self[field_name].jsonify(subparser)
|
|
||||||
elif field_type in ("many2one", "reference"):
|
|
||||||
if self[field_name]:
|
|
||||||
value = self[field_name].jsonify(subparser)[0]
|
|
||||||
else:
|
|
||||||
# TODO: we should get this by field (eg: char field -> "")
|
|
||||||
value = None
|
value = None
|
||||||
|
if callable(subparser):
|
||||||
|
# a simple function
|
||||||
|
value = subparser(self, field_name)
|
||||||
|
elif isinstance(subparser, str):
|
||||||
|
# a method on the record itself
|
||||||
|
method = getattr(self, subparser, None)
|
||||||
|
if method:
|
||||||
|
value = method(field_name)
|
||||||
else:
|
else:
|
||||||
raise UserError(_("Wrong parser configuration"))
|
self._jsonify_bad_parser_error(field_name)
|
||||||
|
else:
|
||||||
|
field = self._fields[field_name]
|
||||||
|
if not (field.relational or field.type == "reference"):
|
||||||
|
self._jsonify_bad_parser_error(field_name)
|
||||||
|
rec_value = self[field_name]
|
||||||
|
value = rec_value.jsonify(subparser) if rec_value else []
|
||||||
|
if field.type in ("many2one", "reference"):
|
||||||
|
value = value[0] if value else None
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def _jsonify_bad_parser_error(self, field_name):
|
||||||
|
raise UserError(_("Wrong parser configuration for field: `%s`") % field_name)
|
||||||
|
|
|
@ -39,5 +39,15 @@ can define your mapping as follow into the parser definition:
|
||||||
('line_id:lines', ['id', ('product_id', ['name']), 'price_unit'])
|
('line_id:lines', ['id', ('product_id', ['name']), 'price_unit'])
|
||||||
]
|
]
|
||||||
|
|
||||||
|
If you need to parse the value of a field in a custom way,
|
||||||
|
you can pass a callable or the name of a method on the model:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
parser = [
|
||||||
|
('name', "jsonify_name") # method name
|
||||||
|
('number', lambda rec, field_name: rec[field_name] * 2)) # callable
|
||||||
|
]
|
||||||
|
|
||||||
Also the module provide a method "get_json_parser" on the ir.exports object
|
Also the module provide a method "get_json_parser" on the ir.exports object
|
||||||
that generate a parser from an ir.exports configuration.
|
that generate a parser from an ir.exports configuration.
|
||||||
|
|
|
@ -5,6 +5,10 @@ from odoo import fields
|
||||||
from odoo.tests.common import TransactionCase
|
from odoo.tests.common import TransactionCase
|
||||||
|
|
||||||
|
|
||||||
|
def jsonify_custom(self, field_name):
|
||||||
|
return "yeah!"
|
||||||
|
|
||||||
|
|
||||||
class TestParser(TransactionCase):
|
class TestParser(TransactionCase):
|
||||||
def test_getting_parser(self):
|
def test_getting_parser(self):
|
||||||
expected_parser = [
|
expected_parser = [
|
||||||
|
@ -114,10 +118,6 @@ class TestParser(TransactionCase):
|
||||||
|
|
||||||
self.assertDictEqual(json_partner[0], expected_json)
|
self.assertDictEqual(json_partner[0], expected_json)
|
||||||
|
|
||||||
json_partner = partner.jsonify(parser)
|
|
||||||
|
|
||||||
self.assertDictEqual(json_partner[0], expected_json)
|
|
||||||
|
|
||||||
# Check that only boolean fields have boolean values into json
|
# Check that only boolean fields have boolean values into json
|
||||||
# By default if a field is not set into Odoo, the value is always False
|
# By default if a field is not set into Odoo, the value is always False
|
||||||
# This value is not the expected one into the json
|
# This value is not the expected one into the json
|
||||||
|
@ -127,3 +127,38 @@ class TestParser(TransactionCase):
|
||||||
expected_json["lang"] = None
|
expected_json["lang"] = None
|
||||||
expected_json["children"] = []
|
expected_json["children"] = []
|
||||||
self.assertDictEqual(json_partner[0], expected_json)
|
self.assertDictEqual(json_partner[0], expected_json)
|
||||||
|
|
||||||
|
def test_json_export_callable_parser(self):
|
||||||
|
# Enforces TZ to validate the serialization result of a Datetime
|
||||||
|
partner = self.env["res.partner"].create(
|
||||||
|
{
|
||||||
|
"name": "Akretion",
|
||||||
|
"country_id": self.env.ref("base.fr").id,
|
||||||
|
"lang": "en_US", # default
|
||||||
|
"category_id": [(0, 0, {"name": "Inovator"})],
|
||||||
|
"child_ids": [
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
"name": "Sebatien Beau",
|
||||||
|
"country_id": self.env.ref("base.fr").id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
"date": fields.Date.from_string("2019-10-31"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
partner.__class__.jsonify_custom = jsonify_custom
|
||||||
|
parser = [
|
||||||
|
# callable subparser
|
||||||
|
("name", lambda rec, fname: rec[fname] + " rocks!"),
|
||||||
|
("name:custom", "jsonify_custom"),
|
||||||
|
]
|
||||||
|
expected_json = {
|
||||||
|
"name": "Akretion rocks!",
|
||||||
|
"custom": "yeah!",
|
||||||
|
}
|
||||||
|
json_partner = partner.jsonify(parser)
|
||||||
|
self.assertDictEqual(json_partner[0], expected_json)
|
||||||
|
del partner.__class__.jsonify_custom
|
||||||
|
|
Loading…
Reference in New Issue