浅析 odoo用户视图中权限字段的实现
设置-用户中编辑用户信息是我们常用到的功能,但是当我们在开发者权限下查看用户form视图字段时,里面访问权限字段设置却显示如下:
这确实很让人困惑.
相关知识
在Ruter的这篇权限组的设置中介绍了权限组的实现.
从这篇文章的实现中我们可以很直观了解到在xml中设置model="ir.module.category"
的record,用户视图中权限字段的显示会多出一个新分类,在分类下可以选择他们的子项(也就是model="res.groups"
的record),当选择完时,可以发现用户被加入到了相关权限组中.
看到这里,也许你已经大概猜到了我接下来要说什么了.
odoo用户视图中权限字段的实现
在addons/base/res/res_users_view.xml
中我们可以发现id="view_users_form"
的record,是用户表单视图的基本实现,通过对比源码以及odoo开发者模式下的字段视图获取,他们之间的主要差别是源码中的
被替换成了开头所说的让人费解的字段.
在继续从源码中查找,我们还可以发现另外一段在同文件下继承了id="view_users_form"
的一个id="user_groups_view"
的record.在这段record中,主要定义了
也就是说groups_id
的字段的显示部分已经id="user_groups_view"
被替换了.
从开发者模式下进入设置-页面-视图 中搜索这个id,可以发现这个视图的结构完全与被替换掉
的用户视图一致.
但是,在XML中,id = user_groups_view
的record中并没有定义出具体字段,那么odoo是如何精确的把相关分组设置加入到用户的视图呢?
在addons/base/res/res_users.py
中发现了一个继承了res.groups
的类GroupsView
里面存在一个方法
_update_user_groups_view
,在这个方法的介绍里,作者介绍了作用:
Modify the view with xmlid
base.user_groups_view
, which inherits
the user form view, and introduces the reified group fields.
下面给出这个方法的代码:
@api.model
def _update_user_groups_view(self):
""" Modify the view with xmlid ``base.user_groups_view``, which inherits
the user form view, and introduces the reified group fields.
"""
if self._context.get('install_mode'):
# use installation/admin language for translatable names in the view
user_context = self.env['res.users'].context_get()
self = self.with_context(**user_context)
# We have to try-catch this, because at first init the view does not
# exist but we are already creating some basic groups.
view = self.env.ref('base.user_groups_view', raise_if_not_found=False)
if view and view.exists() and view._name == 'ir.ui.view':
group_no_one = view.env.ref('base.group_no_one')
xml1, xml2 = [], []
xml1.append(E.separator(string=_('Application'), colspan="2"))
for app, kind, gs in self.get_groups_by_application():
# hide groups in categories 'Hidden' and 'Extra' (except for group_no_one)
attrs = {}
if app.xml_id in ('base.module_category_hidden', 'base.module_category_extra', 'base.module_category_usability'):
attrs['groups'] = 'base.group_no_one'
if kind == 'selection':
# application name with a selection field
field_name = name_selection_groups(gs.ids)
xml1.append(E.field(name=field_name, **attrs))
xml1.append(E.newline())
else:
# application separator with boolean fields
app_name = app.name or _('Other')
xml2.append(E.separator(string=app_name, colspan="4", **attrs))
for g in gs:
field_name = name_boolean_group(g.id)
if g == group_no_one:
# make the group_no_one invisible in the form view
xml2.append(E.field(name=field_name, invisible="1", **attrs))
else:
xml2.append(E.field(name=field_name, **attrs))
xml2.append({'class': "o_label_nowrap"})
xml = E.field(E.group(*(xml1), col="2"), E.group(*(xml2), col="4"), name="groups_id", position="replace")
xml.addprevious(etree.Comment("GENERATED AUTOMATICALLY BY GROUPS"))
xml_content = etree.tostring(xml, pretty_print=True, xml_declaration=True, encoding="utf-8")
view.with_context(lang=None).write({'arch': xml_content})
这段代码即便没仔细的阅读,我们也可以很轻松的了解到大体功能:得到所有application(也就是categroy),
构造出field,其中field会根据该选项是否是selection
类型以及其他处理(意味着选项是boolean
)
最后,把构造好的XML重新写入数据库中id = user_groups_view
的记录.
再仔细阅读,可以发现方法中循环的主题是通过for app, kind, gs in self.get_groups_by_application():
这段代码生产的.这段代码作者也给出了相关介绍:
Return all groups classified by application (module category), as a list::
[(app, kind, groups), ...],
whereapp
andgroups
are recordsets, andkind
is either
'boolean'
or'selection'
. Applications are given in sequence
order. Ifkind
is'selection'
,groups
are given in
reverse implication order.
至于看起来很奇怪的字段名,在源码中有一段name_selection_groups
的方法:
def name_selection_groups(ids):
return 'sel_groups_' + '_'.join(map(str, ids))
可以发现_update_user_groups_view
中在对字段处理的时候调用了该方法,所以field字段显示的逻辑是各种不同权限中包含的的groups_id的组合.(对应implied_ids
字段,分组包含其他分组的权限)
_update_user_groups_view
方法会在页面升级的时候调用一次生成视图,此外,每当res.groups
模型中执行了创建,删除,修改操作也会同时执行该段函数更新视图
应用
知道了原理,操作这部分就变得十分轻而易举了.首先继承res.groups模块,然后重写_update_user_groups_view
方法
@api.model
def _update_user_groups_view(self):
super(SpeGroupsView, self)._update_user_groups_view()
# 修改base.user_groups_view视图,把Admin设置只分配给超级管理员
view = self.env.ref('base.user_groups_view', raise_if_not_found=False)
etree = ET.fromstring(view.arch)
change_field = etree.find(".//field[@name='sel_groups_1_2']")
if change_field is not None:
change_field.set('groups', 'base.group_system')
xml_content = ET.tostring(etree, encoding='utf-8', method='xml')
view.with_context(lang=None).write({'arch': xml_content})
这段代码中等odoo的原本的处理完毕之后,我们再取出来,根据自己的需要操作XML(就跟平时编写xml一样),
在这里,我对 系统管理
字段设置了groups,只有group_system
权限组才可以看的到该视图.
然后重新与原方法一样,重新写回数据库中.