[IMP] base: add native support for ir.config_parameter in res.config

Res config currently have some native behavior to support features
like default_XX (setting global default value), group_XX (adding
implied groups) and module_XX (installing or removing modules). This
commit add support of config_parameter attribute on other fields that
automatically links a config parameter to the field. It allows to
hide the set and get of config parameter and their value conversion
as everything is now handled by the base settings class.

Thanks to @rco-odoo for his help and review when working on this
feature.
This commit is contained in:
Richard Mathot
2018-01-16 13:34:29 +01:00
committed by Thibault Delavallée
parent 66aefb1723
commit a0dd81bfab
+67 -7
View File
@@ -4,10 +4,9 @@ import json
import logging
import re
from operator import attrgetter, add
from lxml import etree
from odoo import api, models, registry, SUPERUSER_ID, _
from odoo import api, models, _
from odoo.exceptions import AccessError, RedirectWarning, UserError
from odoo.tools import ustr
@@ -341,6 +340,7 @@ class ResConfigSettings(models.TransientModel, ResConfigModuleInstallationMixin)
default_foo = fields.type(..., default_model='my.model'),
group_bar = fields.Boolean(..., group='base.group_user', implied_group='my.group'),
module_baz = fields.Boolean(...),
config_qux = fields.Char(..., config_parameter='my.parameter')
other_field = fields.type(...),
The method ``execute`` provides some support based on a naming convention:
@@ -366,14 +366,18 @@ class ResConfigSettings(models.TransientModel, ResConfigModuleInstallationMixin)
``execute`` triggers the immediate installation of the module named 'XXX'
if the field has the integer value ``1``.
* For a field with no specific prefix BUT an attribute 'config_parameter',
``execute``` will save its value in an ir.config.parameter (global setting for the
database).
* For the other fields, the method ``execute`` invokes all methods with a name
that starts with 'set_'; such methods can be defined to implement the effect
of those fields.
The method ``default_get`` retrieves values that reflect the current status of the
fields like 'default_XXX', 'group_XXX' and 'module_XXX'. It also invokes all methods
with a name that starts with 'get_default_'; such methods can be defined to provide
current values for other fields.
fields like 'default_XXX', 'group_XXX', 'module_XXX' and config_XXX.
It also invokes all methods with a name that starts with 'get_default_';
such methods can be defined to provide current values for other fields.
"""
_name = 'res.config.settings'
@@ -437,6 +441,7 @@ class ResConfigSettings(models.TransientModel, ResConfigModuleInstallationMixin)
{ 'default': [('default_foo', 'model', 'foo'), ...],
'group': [('group_bar', [browse_group], browse_implied_group), ...],
'module': [('module_baz', browse_module), ...],
'config': [('config_qux', 'my.parameter'), ...],
'other': ['other_field', ...],
}
"""
@@ -444,7 +449,7 @@ class ResConfigSettings(models.TransientModel, ResConfigModuleInstallationMixin)
Groups = self.env['res.groups']
ref = self.env.ref
defaults, groups, modules, others = [], [], [], []
defaults, groups, modules, configs, others = [], [], [], [], []
for name, field in self._fields.items():
if name.startswith('default_') and hasattr(field, 'default_model'):
defaults.append((name, field.default_model, name[8:]))
@@ -456,10 +461,18 @@ class ResConfigSettings(models.TransientModel, ResConfigModuleInstallationMixin)
elif name.startswith('module_') and field.type in ('boolean', 'selection'):
module = IrModule.sudo().search([('name', '=', name[7:])], limit=1)
modules.append((name, module))
elif hasattr(field, 'config_parameter'):
if field.type in ('boolean', 'char', 'selection', 'many2one', 'integer', 'float'):
configs.append((name, field.config_parameter))
else:
msg = "Error with field %s !" \
"Automatic ir.config_parameter getting/setting is not implemented for %s's." \
% (name, field.type)
raise Exception(msg)
else:
others.append(name)
return {'default': defaults, 'group': groups, 'module': modules, 'other': others}
return {'default': defaults, 'group': groups, 'module': modules, 'config': configs, 'other': others}
def get_values(self):
"""
@@ -470,6 +483,7 @@ class ResConfigSettings(models.TransientModel, ResConfigModuleInstallationMixin)
@api.model
def default_get(self, fields):
IrDefault = self.env['ir.default']
IrConfigParameter = self.env['ir.config_parameter'].sudo()
classified = self._get_classified_fields()
res = super(ResConfigSettings, self).default_get(fields)
@@ -492,6 +506,36 @@ class ResConfigSettings(models.TransientModel, ResConfigModuleInstallationMixin)
if self._fields[name].type == 'selection':
res[name] = int(res[name])
# config: get & convert stored ir.config_parameter (or default)
WARNING_MESSAGE = "Error when converting value %r of field %s for ir.config.parameter %r"
for name, icp in classified['config']:
field = self._fields[name]
value = IrConfigParameter.get_param(icp, field.default(self) if field.default else False)
if value is not False:
if field.type == 'many2one':
try:
# Special case when value is the id of a deleted record, we do not want to
# block the settings screen
value = self.env[field.comodel_name].browse(int(value)).exists().id
except ValueError:
_logger.warning(WARNING_MESSAGE, value, field, icp)
value = False
elif field.type == 'integer':
try:
value = int(value)
except ValueError:
_logger.warning(WARNING_MESSAGE, value, field, icp)
value = 0
elif field.type == 'float':
try:
value = float(value)
except ValueError:
_logger.warning(WARNING_MESSAGE, value, field, icp)
value = 0.0
elif field.type == 'boolean':
value = value.lower() == 'true'
res[name] = value
# other fields: call the method 'get_values'
# The other methods that start with `get_default_` are deprecated
for method in dir(self):
@@ -536,6 +580,22 @@ class ResConfigSettings(models.TransientModel, ResConfigModuleInstallationMixin)
groups.write({'implied_ids': [(3, implied_group.id)]})
implied_group.write({'users': [(3, user.id) for user in groups.mapped('users')]})
# config fields: store ir.config_parameters
IrConfigParameter = self.env['ir.config_parameter'].sudo()
for name, icp in classified['config']:
field = self._fields[name]
value = self[name]
if field.type == 'char':
# storing developer keys as ir.config_parameter may lead to nasty
# bugs when users leave spaces around them
value = (value or "").strip() or False
elif field.type in ('integer', 'float'):
value = repr(value) if value else False
elif field.type == 'many2one':
# value is a (possibly empty) recordset
value = value.id
IrConfigParameter.set_param(icp, value)
# other fields: execute method 'set_values'
# Methods that start with `set_` are now deprecated
for method in dir(self):