全书完整目录请见:Odoo 14开发者指南(Cookbook)第四版
大多数情况下,Odoo由多用户组织使用。每个组织中的每个用户有着不同的角色,根据角色拥有不同的权限。例如,HR主管无法访问公司的财务信息。通过访问权限和记录规则,可以指定用户能够访问Odoo中哪些信息。本章中,我们将学习如何指定访问权限规则和记录规则。
这种权限安全的区划要求按照权限级别来提供角色的访问权限。本章中将进行学习。
本章中,我们将学习如下小节的内容:
- 创建权限组并为用户分配组
- 向模型添加访问权限
- 限制模型中字段的访问
- 使用记录规则限制记录访问
- 使用权限组来启用功能
- 以超级用户访问记录集
- 根据组来隐藏视图元素和菜单
为避免讲解时的冗杂,本章中各小节对已有模块进行了一些微调。
技术准备
本章的技术要求包含使用第三章 创建Odoo插件模块各小节所创建的模块。在继续本章的示例之前应创建该模块并做好使用的准备。
本章中的所有代码可通过 GitHub 仓库进行下载:https://github.com/alanhou/odoo14-cookbook。
创建安全组并分配用户
Odoo中的访问权限通过安全组进行配置:给组指定权限,然后对组添加用户。每个功能区都有中央应用所提供的基础安全组。
在插件继承已有应用时,应对相应的组添加权限,参见本章稍后的向模型添加访问权限一节。
在插件模块中添加中央应用所未涵盖的新功能区时,应添加相应的安全组。通常,至少应该有用户和管理员这两个角色。
以第三章 创建Odoo插件模块中所介绍的图书应用为例,它与Odoo的核心应用并没有很好的融合,下面我们来对其添加安全组。
准备工作
本节假定你已经准备好了Odoo实例,并且有第三章 创建Odoo插件模块中所讲解的my_library模块。
如何实现…
执行如下步骤,向模块添加新的访问权限组:
译者注:升级完代码看不到菜单的先不要着急,不是代码的问题,请耐心往下看
- 确保插件模块的__manifest__.py声明文件中定义有category键:
1'category': 'Library', - 在声明文件的data键中新增security/groups.xml文件:
1234'data': ['security/groups.xml','views/library_book.xml',], - 在security/library_security.xml中新增数据记录的XML文件,先使用一个空的结构:
1234<?xml version="1.0" encoding="utf-8"?><odoo><!-- Data records go here --></odoo> - 在XML数据元素中添加两个新组的record标签:
12345678910111213141516<record id="group_library_user" model="res.groups"><field name="name">User</field><field name="category_id"ref="base.module_category_library"/><field name="implied_ids" eval="[(4,ref('base.group_user'))]"/></record><record id="group_library_librarian" model="res.groups"><field name="name">Librarians</field><field name="category_id"ref="base.module_category_library"/><field name="implied_ids" eval="[(4,ref('group_library_user'))]"/><field name="users" eval="[(4, ref('base.user_admin'))]"/></record>
如果我们升级该插件模块,会加载这两条记录。想在用户界面中看到这些组的话,需要启用开发者模式。然后就可以通过Settings > Users > Groups菜单项进行查看了,如下:
📝重要信息:新增的模型,admin用户没有访问权限。也就是说对该模型添加的菜单和视图对admin用户都是不可见的。要想显示,需要添加该模型的权限,在向模型添加访问权限一节中会进行讲解。顺便说一睛,可以通过超级用户(superuser)来访问新增的模型,想了解如何实现,请参见第三章 创建Odoo插件模块中的以超级用户访问 Odoo一节。
运行原理…
插件模块以功能区或主应用进行划分,如财务和金融、销售或人力资源。它们在声明文件中以category键进行定义。
如果category名称不存在,会进行自动创建。为方便起见,会为新的分类名创建一个小写的base.module_category_<声明文件中分类名称>的XML ID,并将空格替换为下划线。 这可以用于通过应用分类来关联安全组。
本例中,我们使用了Library分类名,它生成了一个base.module_category_library的XML标识符。
按照惯例,包含权限相关元素的数据文件应放在security子目录内。
还需要在声明文件中注册权限文件。模块声明中data键中文件的顺序非常重要,因为无法在其它视图或ACL文件中引用还未定义的组。最好将权限数据文件放在ACL文件和其它用户界面数据文件列表的最上面。
本例中,我们使用<record>标签创建了组,它会创建一条res.groups模型记录。res.groups模型中最重要的字段如下:
- name:这是组的显示名。
- category_id:这是对应用分类的引用,用于在用户表单中组织分组。
- implied_ids:它们是所要继承权限的其它组。
- users:这是属于该组的用户。在新插件模块中,我们通常希望admin用户归属应用的管理员组。
第一个安全组使用了implied_ids来作为base.group_user组。它是Employee用户组,是所有后台用户所会共享的基本安全组。
第二个安全组在users字段上设置值来将其分配给管理员用户,其XML ID为base.user_admin。
某个安全组的用户会自动归属于其所继承的组。例如为任意用户分配Librarians组,该用户会自动的包含在User组中,因为Librarian组的implied_ids字段中有User组。
同时,安全组所授予的安全权限是累加的。用户具有他所属于的任意组(直接授权或通过继承授权)的权限。
有些安全组在用户表单中显示为选择框而非单个复选框。这种情况发生在所涉及的组在同一应用分类中并且通过implied_ids线性相交。例如,Group A继承Group B,Group B继承Group C。如果组没有通过implied_ids与任何其它组相关联,我们看到的就不是选择框,而是复选框。
📝注:注意在前面字段中定义的关联还存在反向关联,可在关联模型中进行编辑,如安全组和用户。
设置category_id和implied_ids等引用字段的值通过使用关联记录的XML ID和一些特殊的语法完成。这种语法在第六章 管理模块数据中进行了详细讲解。
扩展知识…
另外值得注意的是名为Extra Rights的特别安全组base.group_no_one。在早期的Odoo版本中,它用于一些默认隐藏的功能,仅在激活了Technical Features标记时才进行显示。从9.0版本开始发生了一些改变,只要开启开发者模式就会显示这些功能。
通过安全组授予的访问权限仅可以进行叠加。无法禁用某个组所授予的权限。这表示手动创建的用于自定义权限的组应继承最接近又少于所需权限的组(若有),然后添加所有需要的剩余权限。
组中还可以有以下的额外字段:
- 菜单(menu_access字段):指定组所能访问的菜单项
- 视图(view_access字段):指定组所能访问的UI视图
- 访问权限(model_access字段):指定组所能访问的模型,在为模型添加访问权限一节中会进行详述
- 访问规则(rule_groups字段):一些应用于组的记录级权限规则,在限制模型中字段的访问一节中会进行详述
- 提示(comment字段):这是组的描述或注释文本
本节中,我们学习了如何创建权限组并通过图形界面进行授权。接下来的几节中,我们将使用这些组来创建访问控制列表和记录规则。
其它内容
想了解如货号通过超级用户访问新增模型,请参见第三章 创建Odoo插件模块中的以超级用户访问 Odoo一节。
为模型添加访问权限
为插件模块新增模型非常普遍。例如在第三章 创建Odoo插件模块中,我们新增了一个Library Books模型。在开发过程中很容易漏掉对新模型安全权限的创建,这时会很难看到所创建的菜单和视图,因为从Odoo 12起,admin用户默认对新模型没有访问权限。要看到新模型的视图和菜单,需要添加权限访问控制列表(ACL)。
但是没有ACL的模型在加载时会触发警告日志消息,告知用户所漏掉的ACL定义。
1 |
WARNING The model library.book has no access rules, consider adding one example, access_library_book, access_library_book, model_library_book, base.group_user,1,0,0,0 |
可以通过超级用户访问新增的模型,它不受任何权限规则所限制。更多相关知识,请参见第三章 创建Odoo插件模块的以超级用户访问 Odoo一节。超级用户的功能仅有管理员(administrator)用户可用。因此,非管理员用户如需要使用新模型,需要为他们定义访问权限列表,这样Odoo就知道他们能以何种方式访问以及每个用户组所能执行的操作有哪些。
准备工作
我们将继续使用前一小节中所创建的my_library模块,并为其添加所漏掉的ACL。
如何实现…
my_library中应该已经包含了创建library.book模型的models/library_book.py这一Python文件。现在我们会执行如下步骤添加一个描述该模型的权限访问控制的数据文件:
- 编辑__manifest__.py文件来声明一个新的数据文件:
12345data: [# ...权限组'security/ir.model.access.csv',# ...其它数据文件] - 在模块中新增一个包含如下内容的security/ir.model.access.csv文件:
123id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlinkacl_book,library.book_default,model_library_book,group_library_user,1,0,0,0acl_book_librarian,library.book_librarian,model_library_book,group_library_librarian,1,1,1,1
应当先升级模块来把这些ACL记录添加到我们的Odoo数据库中。更重要的是,如果我们使用demo用户登录到演示数据库中,应该可以访问Library菜单项且不会出现任何权限报错。
译者注:此处表述有误,demo用户需要添加到group_library_user中才能看到菜单(原因可能是早前版本中使用base_group_user直接作为普通用户组,此处未更新),如下图所示:
运行原理…
权限控制访问列表存储在核心的ir.model.access模型中。我们只需要添加描述针对每个用户组访问权限的记录。
任何类型的数据文件都可以,但通常是使用CSV文件。该文件可放在插件模块目录中的任何位置,但惯例是将所有安全权限相关的文件放到security子目录中。
本节中的第一步向声明文件添加了这个数据文件。第二步添加了一个描述权限访问控制规则的文件。CSV文件必须按照所要加载记录的模型来进行命名,因此所使用名称不止是一种惯例而是强制如此:参见第六章 管理模块数据获取更多信息。
如果模块还创建了新的安全组,它的数据文件应在声明文件的ACL数据文件之前进行声明,因为在ACL中可能会使用到它们。在处理ACL文件时它们必须是已经创建了的。
CSV文件中的字段如下:
- id:这是该规则的XML ID内部标识符。模块中的任意唯一名称都可以,但按惯例使用access_<model>_<group>。
- name:这是访问规则的标题。通常是使用access.<model>.<group>这一名称。
- model_id:id:这是该模型的XML ID。Odoo使用model_<name>格式对这些模型自动分配这种类型的ID,使用带下划线而非点号的模型名称。如果模型是在另一个模块中创建的,则需要包含模块名称的有效完整XML ID。
- group_id:id:这是针对用户组的XML ID。如果留空,则用于所有用户。base模块中提供了一些基本用户组,比如针对所有员工的base.group_user和针对管理员用户的base.group_system。其它应用可以添加自己的用户组。
- perm_read:该行中组的成员可以读取模型记录。它接收两个值:0或1。使用0来禁用模型的读权限,1来允许读的权限。
- perm_write:该行中组的成员可以更新模型记录。它接收两个值:0或1。使用0来禁止模型的写权限,1来允许写的权限。
- perm_create:该行中组的成员可以在该模型中新增记录。它接收两个值:0或1。使用0来禁止模型的创建权限,1来允许创建的权限。
- perm_unlink:该行中组的成员可以在该模型中删除记录。它接收两个值:0或1。使用0来禁止模型的删除权限,1来允许删除的权限。
我们所使用的CSV文件对Employees > Employee标准安全组添加了只读权限并向Administration > Settings 组添加了全部的写权限。
Employee用户组base.group_user尤为重要,因为Odoo标准应用所添加的用户组都会继承它。这表示如果我们需要让所有后台用户都能访问新模型的话,不管它们所配合使用的具体应用是什么,都应将权限在Employee组中进行添加。
结果产生的ACL可在调试模式下通过访问Settings > Technical > Security >Access Controls List 来在图形界面中进行查看,如下图所示:
图10.2 – ACL列表视图
有些人觉得通过用户界面定义ACL然后用导出功能生成CSV文件会更为容易。
扩展知识…
将这个权限授予创建安全组并分配用户一节中所定义的Library user和Librarian组会显而易见。如果你学习了该节,再跟着本节操作会是很好的练习,将组标识符调整为图书对应的组。
要重点注意不应直接对插件模块的访问列表进行自定义,因为它们会在下一次模块升级时重新加载,去除在用户界面中所做的任何自定义内容。
可采用两种方法来自定义 ACL。一种是创建继承模块中所提供组的新安全组并对其添加新的权限,但这样我们只能添加权限,不能删除权限。更灵活的方法是对具体的ACL行取消Active标记勾选以进行禁用。默认active字段不可见,因此我们需要编辑列表视图来添加<field name=”active” />字段。我们还可以添加新的ACL行来添加或替换权限。在模块升级时,禁用的ACL不会重新激活,添加的ACL行也不会受到影响。
另外值得注意的是ACL仅用于普通模型,无需对抽象模型或临时模型进行定义。如若定义,也会被忽略,在服务端日志中会打出警告信息。
限制模型中字段的访问
在某些情况下,我们可能会需要更细粒度的访问控制,可能会需要限制对模型具体字段的访问。
可以使用groups属性来让字段仅能由指定的安全组所访问。下面会展示如何在图书模型中添加限制访问的字段。
准备工作
我们将继续使用前一节中的my_library模块。
如何实现…
添加仅针对特定安全组有访问权限的字段,可执行如下步骤:
- 编辑模型文件添加字段:
12is_public =fields.Boolean(groups='my_library.group_library_librarian')private_notes =fields.Text(groups='my_library.group_library_librarian') - 编辑XML文件中的视图来添加该字段:
12<field name="is_public" /><field name="private_notes" />
这样就好了。现在对插件模块进行升级来让模型中的修改生效。如果使用没有系统配置权限的用户登录的话,比如用 demo 用户登录带有演示数据的数据库,图书表单中就不会显示该字段。
运行原理…
带有groups属性的字段进行了特殊处理,它检查用户是否属于属性中所指定的任意安全组。如果用户不属于指定的组,Odoo会在用户界面中去除该字段并限制对该字段的ORM操作。
注意这并不仅仅是流于表面。该字段不仅在用户界面中会隐藏,同时在用户的其它ORM操作中也无法使用它,比如读和写。对于XML-RPC或JSON-RPC调用同样如此。
在业务逻辑或用户界面on-change事件(@api.onchange方法)中使用这些字段时需要小心,在用户没有该字段访问权限时会抛出错误。一种避免的方式是使用提权,比如sudo()模型方法或计算字段的compute_sudo字段属性。
groups的值是包含安全组的有效XML ID逗号分隔列表的一个字符串。查找一个指定组XML ID最简单的方式是激活开发者模式并通过Settings > Users & Companies > Groups导航到该组的表单,然后通过调试菜单访问View Metadata选项,如下图所示:
也可以通过创建组的<record>标签来在代码中查看安全组的XML ID。但最方便的方式还是向上图这样通过元数据来查看组的XML ID。
扩展知识…
有些情况下,我们需要根据具体条件来让字段可用或不可用,像字段中的值,比如stage_id或state。通常使用states或attrs属性来在视图级别进行处理来根据特定条件动态显示或隐藏字段。参见第九章 后端视图获取更多详情。
注意这些技巧仅作用于用户界面,并不是实际的访问权限。要实现访问权限控制,需要在业务逻辑层添加检查。要么添加@constrains所装饰的模型方法,实现具体的验证,要么对验证逻辑扩展create, write或unlink方法。在第五章 基本服务端开发中可以获取更多的相关知识。
其它内容
有关如何使用条件来隐藏有显示字段的详情,请参见第九章 后端视图。
有关业务逻辑层的更多知识,请参见第五章 基本服务端开发。
使用记录规则限制记录的访问
对一个应用的常见需求是在指定模型中限制每个用户所能使用的记录。
这通过使用记录规则来实现。记录规则是一个定义在模型上的域过滤器表达式,然后添加到所受影响的用户每次数据查询中。
作为示例,我们将对图书模型添加记录规则来让Employee组中的用户仅能访问公共图书。
准备工作
我们将继续使用前一节中的my_library模块。
如何实现…
记录规则使用XML数据文件进行添加。执行如下步骤来进行实现:
- 在声明文件的 data 键中添加security/library_security.xml文件:
1234'data': ['security/library_security.xml',# ...], - 应当有一个security/library_security.xml 数据文件,在<odoo>代码块中创建安全组:
12345678910111213141516171819<odoo noupdate="1"><record model="ir.rule" id="library_book_user_rule"><field name="name">Library: see only own books</field><field name="model_id" ref="model_library_book"/><field name="groups" eval="[(4,ref('my_library.group_library_user'))]"/><field name="domain_force">[('is_public', '=', True)]</field></record><record model="ir.rule" id="library_book_all_rule"><field name="name">Library: see all books</field><field name="model_id" ref="model_library_book"/><field name="groups"eval="[(4,ref('my_library.group_library_librarian'))]"/><field name="domain_force">[(1, '=', 1)]</field></record></odoo>
升级插件模块会在Odoo实例中加载记录规则。如果你使用的是演示数据,可以使用默认的 demo 用户来将图书用户权限授予 demo 用户进行测试。如果使用的不是演示数据,可以新建一个带有图书用户权限的用户。
运行原理…
记录规则仅仅是在 ir.rule 核心模型中所加载的数据记录。而添加规则的文件可以放在模块的任意位置,按照惯例会将其放到security子目录中。在同一个XML文件中同时放置安全组和记录规则很常见。
与安全组不同,在标准模块中记录规则是通过noupdate=”1″ 属性加载到odoo代码区中的。借助于它,这些记录不会在模块升级时重新加载,也就是说对它们做的手动自定义在后续升级中是安全并且会得以保留的。
为保持与官方模块的一致,我们也应将记录规则放在<odoo noupdate=”1″>内。
记录规则在用户界面中也可以通过Settings > Technical > Security > Record Rules菜单项进行查看,如下图所示:
图10.4 – 图书模型的ACL
以下是本例中所使用的最重要的记录规则字段:
- 名称(name):针对规则的描述性标题。
- 对象(model_id):对规则所应用模型的引用。
- 组(groups):规则所应用的安全组。如未指定安全组,规则应视作全局并采用不同的方式进行应用(继续阅读本节来学习有关组的更多知识)。
- 域(domain):域表达式用于过滤记录。该记录将仅作用于这些过滤后的记录。
我们所创建的第一条记录规则针对Library User 安全组。它使用[(‘is_public’, ‘=’, True)] 来仅选择公开的图书。因此Library User安全组中的用户仅能看到对外开放的图书。
服务端使用在记录规则中的域表达式运行ORM对象。因此,左侧的字段(元组第一个元素)可使用点号标记。[(‘country_id.code’, ‘=’, ‘IN’)]域表达式会仅显示国家为印度的记录。
因为记录规则多基于当前用户,可以在域中右侧(元组第三个元素)使用用户记录集。因此,如果想要为当前公司的用户显示记录,可以使用 [(‘conpany_id’, ‘=’, user.company_id.id)]域。而如果想要显示由当前用户创建的记录,可以使用[(‘user_id’, ‘=’, user.id)]域。
我们希望Librarian安全组能够看到所有图书,不论是公开的还是对内的。因其继承了Library User组,除非进行相关的操作,否则它将仅能看到公开的图书。
非全局记录规则使用逻辑运算符OR来进行连接,每条规则增加权限且永不会删除该权限。要让Librarian权限组能访问所有图书,须将其添加到记录规则中来为所有图书添加权限,如下:
[(‘is_public’, ‘in’, [True, False])]。
这里我们选择了不同的方式,没有使用无条件访问所有图书记录的规则,而是使用了特殊规则 [(1, ‘=’, 1)]。虽然看起来可能多余,但要记住Library用户规则可以通过另一种方式来不让Settings用户来访问某些图书。域很特别,因为域元组的第一个元素必须为字段名,此例是两种例外情况之一。特殊域[(1, ‘=’, 0)]永不为真,但在记录规则的用例中并没有什么作用,因为使用这种类型的规则会限制对所有记录的访问。在访问控制列表中也可以做类似的事。
📝重要信息:在启用超级用户时会忽略记录规则。测试记录规则时,记得使用其它用户来进行。
扩展知识…
在记录规则未分配给任何安全组时,它被标记为全局并由不同的规则来进行处理。
全局记录规则比组级记录规则影响要更深广,它设置一些无法覆盖的访问限制。技术层面,它们通过AND运算符来进行连接。在标准模块中,它们用于实现多公司安全访问,这样每个用户仅能看到自己公司的数据。
总的来说,普通非全局记录规则通过OR运算符连接在一起,它们在一起添加,只要其中有规则授予权限即才能访问对应的记录。全局记录规则通过使用AND运算符来对普通记录规则添加访问限制。全局记录规则所添加的限制无法由常规记录规则重写。
使用安全组启用功能
安全组可以限制一些功能,这样仅有属于这些组的用户才能访问。安全组也能继承其它组,因此也继承它们的权限。
这两个功能合在一起,用于实现Odoo中的可用性功能:功能切换。安全组也可以用于对Odoo实例中的一些或所有用户启用或禁用功能。
本节展示如何对配置设置添加选项并展示启用额外功能的两种方法,使用安全组让它们可见或通过安装单独的模块来添加功能。
对于第一种情况,我们将会让图书发行日期成为一个可选的附加功能,对于第二种情况,作为示例我们将提供一种选项来安装Notes模块。
准备工作
本节使用第三章 创建Odoo插件模块中所讲解的my_library模块。我们需要使用到安全组,因此还需要按照本章中向模型添加访问权限一节进行操作。
本节中,一些标识符需要引用插件模块的技术名称。我们将假定其为my_library。如果你使用了不同的名称,请将my_library替换为你的插件模块中所使用的实际技术名称。
如何实现…
按照如下给定步骤来添加配置项:
- 要添加所需的依赖和新的XML数据文件,像下面这样编辑__manifest__.py文件并确保它依赖于base_setup:
1234567891011{'name': 'Cookbook code','category': 'Library','depends': ['base_setup'],'data': ['security/ir.model.access.csv','security/groups.xml','views/library_book.xml','views/res_config_settings.xml',],} - 要添加用于启用功能的新安全组,编辑security/groups.xml文件并向其添加如下记录:
1234<record id="group_release_dates" model="res.groups"><field name="name">Library: release date feature</field><field name="category_id" ref="base.module_category_hidden" /></record> - 编辑 models/library_book.py文件中的字段定义,来让图书发行日期仅在启用了该选项时可见:
123456class LibraryBook(models.Model):# ...date_release = fields.Date('Release Date',groups='my_library.group_release_dates',) - 编辑models/__init__.py文件来添加针对配置设置模型的新Python文件:
12from . import library_bookfrom . import res_config_settings - 通过向其添加新选项来继承内核配置向导,添加models/res_config_settings.py文件及如下代码:
123456789from odoo import models, fieldsclass ConfigSettings(models.TransientModel):_inherit = 'res.config.settings'group_release_dates = fields.Boolean("Manage book release dates",group='base.group_user',implied_group='my_library.group_release_dates',)module_note = fields.Boolean("Install Notes app") - 要让选项在用户界面中可以使用,添加views/res_config_settings.xml,扩展设置的表单视图:
1234567891011121314151617<?xml version="1.0" encoding="utf-8"?><odoo><record id="view_general_config_library" model="ir.ui.view"><field name="name">Configuration: add Library options</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"><div id="business_documents" position="before"><h2>Library</h2><div class="row mt16 o_settings_container"><!-- 第7和第8步代码放在这里 --></div></div></field></record></odoo> - 在设置表单视图中,添加增加发行日期功能的选项:
123456789101112<!-- Release Dates option --><div class="col-12 col-lg-6 o_setting_box"><div class="o_setting_left_pane"><field name="group_release_dates" class="oe_inline"/></div><div class="o_setting_right_pane"><label for="group_release_dates"/><div class="text-muted">Enable relase date feature on books</div></div></div> - 在设置表单视图中,添加安装note模块的选项:
123456789101112<!-- Note module option --><div class="col-12 col-lg-6 o_setting_box"><div class="o_setting_left_pane"><field name="module_note" class="oe_inline"/></div><div class="o_setting_right_pane"><label for="module_note"/><div class="text-muted">Install note module</div></div></div>
在升级了这些插件模块之后,在Settings > General > Settings 中两个新的配置选项中应该就可以使用了。参见如下截图:
图10.5 – 通用设置中的Library配置
如上图所示,会出现一个新增的Library设置区。第一个选项Manage book release dates会启用图书记录中的发行日期功能,第二选项Install Notes app会安装Odoo的Notes应用。
运行原理…
内核base模块中有res.config.settings模型,它提供了激活选项背后的业务逻辑。base_setup插件模块使用 res.config.settings模型来提供一些基本配置选项,添加到新数据库中。它还在用户界面中打开了Settings > General Settings菜单。
base_setup模块将res.config.settings适配到中央管理仪表盘上,因此我们需要继承它来添加我们自己的配置设置。
如果我们决定为图书应用创建具体的设置表单,还可以使用一个不同的_name来继承res.config.settings模型,然后为这些设置提供菜单项和表单视图。我们在第八章 高级服务端开发技巧的添加自定义设置选项一节中已进行过相关学习。
我们使用了两种不同的方法来激活这些功能:一种是通过启用安全组并让功能对用户可见,另一种是通过安装一个带有该功能的插件模块。处理这两种用例的逻辑由res.config.settings 模型提供。
本节第一步在依赖中添加了base_setup插件模块,因为它提供对我们想要使用的res.config.settings模型的扩展。还添加了我们所需的额外的XML数据文件来向General Settings表单添加新的选项。
第二步中,我们创建了一个新的安全组,Library: release date feature。功能的启用仅对该组可见,这样在启用该组前都会隐藏。
本例中,我们希望仅在相应的配置项启用时图书发行日期才可用。为进行实现,我们对该字段使用groups属性,这样它仅对这一安全组可用。我们在模型层面进行了操作,它会自动应用到使用该字段的UI视图。
最后,我们继承了res.config.settings模型来添加新选项。每个选项都是一个布尔字段,根据所要实现的功能名称必须以group_或module_开头。
group_选项字段应有一个implied_group属性并且应为一个包含安全组的XML ID逗号分隔列表来在启用时进行激活。XML ID必须是一种完整的形式,包含模块名、点号和标识符名称,如module_name.identifier。
我们还可以添加group属性来指定启用该功能所针对的安全组。如果未定义组,它会对所有基于Employee组的组启用。因此相关组不应用于门户(portal)安全组,因为它们没有像其它普通安全组那样继承Employee基础安全组。
激活背后的机制相当简单:对implied_group添加group属性中安全组,因此让相关的功能对对应的用户可见。
module_选项字段不需要任何的额外属性。字段名的剩余部分标识在启用该选项时安装的模块。在本例中,module_note会安装note模块。
📝重要信息:取消复选框的勾选会卸载该模块并且没有警告,这会导致数据丢失(会导致模模型或字段以及模块数据的删除,译者注:实测 Odoo 14在取消勾选时会有弹窗提示)。为避免不小心取消勾选,secure_uninstall社区模块(来自https://github.com/OCA/server-tools))会在卸载插件模块时弹出要求用户输入密码。
本节的最后一步在Business documents组前添加了通用设置表单视图,即id=”business_documents”。我们使用该id来进行视图继承。它使用模块名作为id创建了自己的div,这是一种良好实践,因为这样继承my_library的其它模块可以轻易地向这个div添加配置项。
扩展知识…
配置设置也可以用带有名为default_前缀的字段。其中有值的话,ORM会将其设置为全局默认值。settings字段应有一个default_model属性,用于标识受影响的模型,default_前缀后的字段名指定具有默认值集合的模型字段。
此外,没有这三种前缀的字段可用于其它设置,但你需要使用get_default_为前缀的方法实现逻辑来获取它们的值,使用set_为前缀的方法来编辑它们的值。
如果想要更深入地了解配置设置,可阅读Odoo的源代码./odoo/addons/base/models/res_config.py,其中有详尽的注释。
以超级用户访问记录集
在前面小节中,我们学习了安全技术,如访问规则、安全组和记录规则。使用这些技术,可以规避未授权的访问。但有时会有复杂的业务用例,其中会需要访问或修改用户没有访问权限的记录。例如,公共用户可能无法访问线索(leads)记录,但通过提交网站的表单,用户可以在后台中生成线索记录。
使用sudo(),可以用超级用户访问记录集。在第八章 高级服务端开发技巧的更改执行动作的用户一节中我们已用到过sudo()。这里我们将学习到即使有给定的ACL规则或对字段添加了安全组,仍然可以通过sudo()进行访问。
如何实现…
我们将使用与前面小节相同的my_library模块。其中已添加了ALC规则,授予了普通用户只读权限。我们还将通过安全组新增字段,这样仅图书管理员能访问它。然后,我们将通过普通用户来修改字段值。按照如下步骤来进行实现:
- 在library.book模型中新增字段:
123report_missing = fields.Text(string="Book is missing",groups='my_library.group_library_librarian') - 在表单视图中添加该字段:
1<field name="report_missing"/> - 在library.book模型中添加report_missing_book()方法:
1234567def report_missing_book(self):self.ensure_one()message = "Book is missing (Reported by: %s)" %self.env.user.nameself.sudo().write({'report_missing': message}) - 在表单视图中添加按钮来在用户界面中触发这个方法,应将其放到<header>标签内:
123<button name="report_missing_book"string="Report Missing Book"type="object"/>
重启服务并更新模块来应用这些修改。
运行原理…
在第1和第2步中,我们分别在模型和表单视图中添加了名为report_missing的新字段。注意我们的Python代码中对该字段添加了my_library.group_library_librarian组,这样仅有Librarian组中的用户才能访问该字段。
接下来的步骤中,我们添加了report_missing_book() 方法,在方法体中更新了report_missing字段的值。注意在调用写方法之前使用了sudo()。
最后,我们在表单视图中添加了一个按钮,用于在用户界面中触发该方法。
要测试这一实现,需要通过非图书管理用户进行登录。如果你使用演示数据加载了数据库的话,可以通过demo用户来登录,然后点击图书表单视图中的Report Missing Book 按钮。点击该按钮时会调用report_missing_book() 方法,这会在report_missing字段中写入消息,虽然该用户并没有相应的权限。可以通过管理员用户来查看该字段值,因为对demo用户该字段是隐藏的。
在点击Report Missing Book按钮时,我们会在report_missing_book()方法中以self参数得到一个当前图书的记录集。在图书记录集中写入值之前,我们使用了self.sudo()。这会返回同一记录集,但用的是超级用户的权限。这个记录集会带有su=True环境属性,因而会跳过访问规则以及记录规则。因而非图书管理员用户可以在图书记录中进行写入。
扩展知识…
在使用sudo()时需要加倍小心,因为它跳过了所有的访问权限。如果你想以另一个用户访问记录的话,可以将该用户的ID传递给sudo,如self.sudo(uid)。这会返回带有该用户环境的记录集。通过这种方法,不会跳过所有访问规则和权限规则,但可以执行授予该用户的所有动作。
根据组来隐藏元素和菜单
在前面小节中,我们学习了如何在Python字段定义中通过group参数来对一些用户隐藏字段。还有一种在用户界面中隐藏字段的方式:通过在视图定义中对XML标签添加安全组。也可以使用安全组来对指定用户隐藏菜单。
准备工作
本节中,我们将复用前一节中的my_library插件模块。在前一节中,我们在<header>标签中添加了一个按钮。通过对其添加groups属性我们可以对一些用户隐藏整个头部。
为book.category模型添加该模型、视图和菜单。我们将对用户隐藏分类菜单。参见第四章 应用模型来学习如何添加模型视图和菜单。
译者注:本节要求代码中包含有library.book.category的相关文件,同时如需验证隐藏 header 的功能,需要在ir.model.access.csv中的group_library_user去除掉留空以便全局用户均可访问。
如何实现…
按照如下步骤来根据安全组隐藏元素:
- 在<header>中添加groups属性来对其它用户隐藏它:
123...<header groups="my_library.group_library_user">... - 在<menuitem>图书分类上添加groups属性来仅对图书用户显示它:
12345<menuitem name="Book Categories"id="library_book_category_menu"parent="library_base_menu"action="library_book_category_action"groups="my_library.group_library_librarian"/>
重启服务并更新模块来应用修改。
运行原理…
第一步中,我们在<header>中添加了groups=”my_library.group_library_user”。这表示整个header部分仅对图书用户和图书管理员可见。不属于group_library_user的普通后台用户则无法看到header部分。
第2步中,我们对菜单项添加了groups=”my_library.group_library_librarian”。这样菜单仅对图书管理员可见。
几乎可以在任何地方使用groups属性,包括<field>, <notebook>, <group>, <menuitems>和视图结构中的任意标签。如果用户不属于该组的Odoo会对其隐藏这些元素。可以在网页和QWeb报表中使用同样的组属性,这将在第十二章 自动化、工作流、Email和打印件和第十四章 CMS网站开发中进行讲解。
正如在本章以超级用户访问记录集一节中所看到的,我们可以在Python字段定义中使用groups参数来对一些用户隐藏字段。注意字段上的安全组和视图中的Python安全组有很大的区别。Python中的安全组提供了真正的权限,非授权用户也无法通过ORM或RPC调用来访问字段。但是视图中的组仅为了提升易用性。XML文件中通过组隐藏的字段仍可以通过RPC或ORM进行访问。
其它内容
参见第四章 应用模型学习如何添加模型视图和菜单。