res.config.setting:源码的解释是:继承自瞬态模型,应用程序设置的基本配置向导。它支持设置默认值、为员工用户分配组和安装模块。
也许你会有疑问:瞬态模型怎么保存数据?
其实他的数据保存机制都是通过调用一些方法,保存在其他模型里面,例如 res_groups,
ir_default ...
接下来让我们看一下这个模型如何使用:
目录
1、默认值
比如说你有一个模型有一个字段设置了他创建时候默认值,即default属性,但是对于某些场景它需要另一个default属性,这时通过继承res.config.settings模型,配置这个字段的默认值选项,该字段以default_开头,属于一个全局默认值,就以原生的销售模块的一个继承模型为例:
然后这个代码在前端界面的体现:
用法:
例如在如下区域为自定义模型的某个字段设置默认值:
实现效果:
demo.py:
from odoo import fields, models
class Demo(models.Model):
_name = 'demo'
name = fields.Char('默认值', default='aaa')
res_config_settings.py
# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
default_name = fields.Selection([
('python', 'python language'),
('java', 'java language')
], 'Demo Name',
default='python',
default_model='demo')
xml:
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="res_config_settings_view_demo_form" model="ir.ui.view">
<field name="name">res.config.demo</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="base_setup.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@id='languages']/div" position="inside">
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_right_pane">
<label for="default_name"/>
<div class="text-muted">
Place choose the default name
</div>
<div class="content-group">
<div class="mt16">
<field name="default_name" class="o_light_label" widget="radio"/>
</div>
</div>
</div>
</div>
</xpath>
</field>
</record>
</odoo>
!注意:继承的视图来自base_setup这个模块,要在__manifest__中加载。
2、安装或者卸载模块
这种字段需要以_module 开头
用法:
python代码:
from odoo import models, fields, api, _
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
default_name = fields.Selection([
('python', 'python language'),
('java', 'java language')
], 'Demo Name',
default='python',
default_model='demo')
module_test_controller = fields.Boolean("Demo")
3、设置系统参数
设置好了之后点击 技术-->系统参数 就能看到
python代码:
from odoo import models, fields, api, _
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
default_name = fields.Selection([
('python', 'python language'),
('java', 'java language')
], 'Demo Name',
default='python',
default_model='demo')
module_test_controller = fields.Boolean("Demo")
demo_config_parameter = fields.Char(config_parameter='demo.config_parameter')
属性:config_parameter就是系统参数中的key
4、设置用户组
这种类型的字段以 group_开头,可以设置group属性,和 implied_group属性,group属性不设置的话默认为 base.group_user,这个字段一般设置为Boolean类型,如果值为True的话,group属性的用户组就拥有 implied_group用户组的所有权限。
from odoo import models, fields, api, _
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
default_name = fields.Selection([
('python', 'python language'),
('java', 'java language')
], 'Demo Name',
default='python',
default_model='demo')
module_test_controller = fields.Boolean("Demo")
demo_config_parameter = fields.Char(config_parameter='demo.config_parameter')
group_demo = fields.Boolean(group='aaa',implied_group='bbb')
以上定义,如果前端勾选了的话,aaa这个组就拥有bbb这个用户组的权限。
5、源码分析
_get_classified_fields方法
目的是对res.config.settings的每一个字段进行类型和属性检查、分类
def _get_classified_fields(self):
# 获取模型对象
IrModule = self.env['ir.module.module']
Groups = self.env['res.groups']
ref = self.env.ref
defaults, groups, modules, configs, others = [], [], [], [], []
# self._fields.items() res.config.settings模型的字段
for name, field in self._fields.items():
if name.startswith('default_'):
# 判断 default开头的字段有没有default_模型这个属性,没有的话就报错
if not hasattr(field, 'default_model'):
raise Exception("Field %s without attribute 'default_model'" % field)
# 添加三元组
defaults.append((name, field.default_model, name[8:]))
# 判断以 group_开头的字段
elif name.startswith('group_'):
# 检查字段类型,必须为 boolean 和selection类型,必须要有implied_group 属性
if field.type not in ('boolean', 'selection'):
raise Exception("Field %s must have type 'boolean' or 'selection'" % field)
if not hasattr(field, 'implied_group'):
raise Exception("Field %s without attribute 'implied_group'" % field)
# 获取字段的group属性,如果没有,默认为base.group_user
field_group_xmlids = getattr(field, 'group', 'base.group_user').split(',')
# 用户组字符串转化为res.groups记录集 例如: res.groups(1,)
field_groups = Groups.concat(*(ref(it) for it in field_group_xmlids))
# 追加三元组 分别为 字段名、用户组、继承的用户组
groups.append((name, field_groups, ref(field.implied_group)))
# 判断字段以 module_开头
elif name.startswith('module_'):
# 这种字段必须为 boolean类型和selection类型
if field.type not in ('boolean', 'selection'):
raise Exception("Field %s must have type 'boolean' or 'selection'" % field)
# 搜索模块,返回一个记录对象 以字段 module_sale_margin为例,搜索名为 sale_margin的模型
module = IrModule.sudo().search([('name', '=', name[7:])], limit=1)
modules.append((name, module))
# 非 以group_、module、default、开头,而有着config_parameter属性的字段
elif hasattr(field, 'config_parameter'):
# 满足以下字段类型
if field.type not in ('boolean', 'integer', 'float', 'char', 'selection', 'many2one'):
raise Exception("Field %s must have type 'boolean', 'integer', 'float', 'char', 'selection' or 'many2one'" % field)
configs.append((name, field.config_parameter))
# 以上条件都不满足
else:
others.append(name)
return {'default': defaults, 'group': groups, 'module': modules, 'config': configs, 'other': others}
execute方法:
点击保存的时候调用
def execute(self):
self.ensure_one()
if not self.env.is_admin():
raise AccessError(_("Only administrators can change the settings"))
self = self.with_context(active_test=False)
# 获取分类之后的字段
classified = self._get_classified_fields()
# 主要对 default_ ,config_parameter,group_的值进行设置
self.set_values()
# module fields: install/uninstall the selected modules
# 要安装的模块
to_install = []
# 要卸载的模块
to_uninstall_modules = self.env['ir.module.module']
lm = len('module_')
for name, module in classified['module']:
# 添加字段值为True的字段和所对应的模型
if int(self[name]):
to_install.append((name[lm:], module))
else:
# 字段的值为False,但是模型已经安装,就需要将其卸载
if module and module.state in ('installed', 'to upgrade'):
to_uninstall_modules += module
# 提交缓存
if to_install or to_uninstall_modules:
self.flush()
if to_uninstall_modules:
# 模块卸载
to_uninstall_modules.button_immediate_uninstall()
# 模块安装
result = self._install_modules(to_install)
# 卸载或者安装之后重新加载环境
if result or to_uninstall_modules:
# After the uninstall/install calls, the registry and environments
# are no longer valid. So we reset the environment.
self.env.reset()
self = self.env()[self._name]
# pylint: disable=next-method-called
config = self.env['res.config'].next() or {}
if config.get('type') not in ('ir.actions.act_window_close',):
return config
# force client-side reload (update user menu and current view)
return {
'type': 'ir.actions.client',
'tag': 'reload',
}
创作不易,觉得有用的小伙伴可以点个赞,也欢迎大家指正不当之处!