业务分析
正常情况下odoo的编辑按钮是不会隐藏的,如果有权限限制会提示权限访问规则错误,也就是产生一个userError,这已经在用户毫不知情的情况下让用户实现了一系列无用的操作,这对用户很不友好,用户只有点击编辑,完成所编辑的内容之后再点击保存才知道无法保存。改进和优化:我们直接将没有权限访问的用户让他看不到编辑按钮就ok了
这样就避免了用户无效的点击,我们以第三方插件的形式
代码实现
注册的文件 manifest.py
{
"name": "Edit button hide",
"summary": "根据权限规则隐藏编辑按钮",
"version": "14.0.1.0.0",
"license": "AGPL-3",
"category": "Web",
"depends": [
"web",
],
"data": [
"views/edit_button_hide.xml",
],
"installable": True,
}
可以看到注册文件只需要一个xml文件,依赖的模块也仅仅是web,这里注意我们的程序要尽量少的依赖其他模块。
前端 js文件
我们可以看看form的展示函数,我们根据页详情案例每次进入的update方法进行跟踪,并重写update方法去添加按钮的展示的逻辑。
odoo.define("web_access_rule_buttons.main", function (require) {
"use strict";
# 应用form控制组件
var FormController = require("web.FormController");
# 重写form控制组件的update方法,update方法属于js异步方法,返回的是promise对象,根据异步回调的方法去获取返回值并进行操作
FormController.include({
async _update(state, params) {
return this._super(state, params).then(this.show_hide_buttons(state));
},
# 按钮显隐的逻辑,获取id值,保证只在编辑的时候作用此规则,并发出rpc请求,进行后端交互
show_hide_buttons: function (state) {
var self = this;
if (state.data.id){
return self
._rpc({
model: this.modelName,
method: "check_access_rule_all",
args: [[state.data.id], ["write"]],
})
.then(function (accesses) {
self.show_hide_edit_button(accesses.write);
});
}
},
#根据后端的返回值进行按钮的显示和隐藏,找到按钮根据条件调用show和hide方法
show_hide_edit_button: function (access) {
if (this.$buttons) {
var button = this.$buttons.find(".o_form_button_edit");
if (button) {
// console.log(access)
if (!access){
button.hide()
}else {
button.show()
}
}
}
},
});
});
后端
我们根据rpc调用后端函数,达到回调显示的作用,后端方法进行权限的校验,并返回相应的参数
from odoo import exceptions, models
class Base(models.AbstractModel):
"""The base model, which is implicitly inherited by all models."""
# 继承基础base模块,添加新方法
_inherit = "base"
# 校验规则
def check_access_rule_all(self, operations=None):
#以下注释是便于规范采用英文翻译,读者如果不理解可以自行利用翻译
#翻译:识别相应操作的权限规则,返回操作的权限bool值,true代表有访问权限,false代表无访问权限
"""Verifies that the operation given by ``operations`` is allowed for
the user according to ir.rules.
If ``operations`` is empty, it returns the result for all actions.
:param operation: a list of ``read``, ``create``, ``write``, ``unlink``
:return: {operation: access} (access is a boolean)
"""
# print(operations)
if not operations or not any(operations):
operations = ["read", "create", "write", "unlink"]
result = {}
for operation in operations:
try:
# 调用自己的校验权限规则方法,传入操作名称
self.check_access_rule(operation)
except exceptions.AccessError:
result[operation] = False
else:
result[operation] = True
return result
渲染文件
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
#继承web.assets_backend:这一资源包在 Odoo 的后台中使用(ERP 部分)。它包含所有与 web
客户端、视图、字段微件、动作管理器等相关的代码
<template
id="assets_backend"
name="edit_button_hide assets"
inherit_id="web.assets_backend"
>
<xpath expr="." position="inside">
<script
type="text/javascript"
src="js文件的路径"
/>
</xpath>
</template>
</odoo>
测试文件
from odoo.tests.common import TransactionCase
# 测试对象和记录的规则
class TestAccessRuleButtons(TransactionCase):
def setUp(self):
super(TestAccessRuleButtons, self).setUp()
self.curr_obj = self.env["res.currency"]
self.curr_record = self.env.ref("base.USD")
def test_check_access_rule_1(self):
res = self.curr_obj.check_access_rule_all(["write"])
self.assertFalse(res["write"])
def test_check_access_rule_2(self):
res = self.curr_record.check_access_rule_all(["write"])
self.assertTrue(res["write"])
def test_check_access_rule_3(self):
res = self.curr_record.check_access_rule_all()
self.assertTrue(res["read"])
self.assertTrue(res["create"])
self.assertTrue(res["write"])
self.assertTrue(res["unlink"])
odoo 14 python 单元测试步骤
一.在模块根目录创建tests目录
二.在tests目录下创建__init__.py文件
三.继承TransactionCase(SingleTransactionCase、SavepointCase)类
四.实现setUp(self, *args, **kwargs)方法
五.以test_前缀为方法名,就是你的测试用例.
在测试用例中调用self.assertEqual方法来断言你的测试用例是否成功执行
六.TransactionCase、SingleTransactionCase、SavepointCase继承这三个不同的类,调用测试用例的情况都不一样
TransactionCase:
1.每一个测试用例是在单独的事务中运行的。
2.一旦测试用例方法运行成功,事务将自动回滚。
3.setUp方法在每次自动调用我们测试用例前都会调用一次,也就是有多少个测试用例就调用多少次setUp。
SingleTransactionCase:
1.所有测试用例都在同一个事务中执行。
2.因此从一个测试用例中产生的数据可以在另一个测试用例中使用。
3.事务从第一个测试用例开始,到最后一个测试用例结束时回滚。
SavepointCase:
1.测试方法运行在回滚的保存点中,而不是将所有的测试用例都放在一个事务中。
2.通过只生成一次测试数据来提高它们的速度。
3.这里,需要使用setUpClass()方法来生成初始化测试数据。
七.对测试类别进行标记.
1.标记可以决定执行测试用例是在安装模块前还是在安装模块后执行。
2.或者你还可以自定义标签。
3.运行的时候你可以指定标记执行测试用例
4.标准标记有:
standard(默认就有):默认会指定执行该标记的测试用例
at_install(默认就有):安装模块后立马执行测试用例
post_install:在所有模块安装完成后再执行测试用例
5.删除默认标记:在标记前面加-减号就代表删除该默认标记
八.注意:所有非标准测试用例将使用–test-enable 选项进行运行。要运行以上的测试用例,需要使用test-tags 选项如下(注意这里我们并不需要显式地传递 –test-enable 选项): 在测试用例的开发过程中,仅对一个模块运行测试用例非常重要。默认,模块的技术名称会添加为、标签,因此你可以使用模块的技术名称加–test-tags 选项。例如,如果你希望对 my_library 模块运行测试用例例如,如果你希望对 my_library 模块运行测试用例。 运行测试用例:./odoo‐bin ‐c server.conf ‐i my_library ‐‐test‐tags=my_library
翻译文件
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module:edit_button_hide
#: model:ir.model,name:edit_button_hide.model_base
msgid "Base"
msgstr ""
总结
思想:做任何一件事情不要盲目的去实现,要有目的,人生在世,愿每个美好都能不期而遇,加油