python odoo 随笔
整理一下用到的内容,想到什么写什么吧,还在完善中
odoo接口
controller
继承odoo的http中controller模型,对函数使用router装饰器
route的参数
route: 路由
type: json或者http
auth:
public: 公开接口,可以直接调用,用户身份为public
user: 内部用户,需要登录才能调用
none: 公开接口,但没有用户,无法访问数据库
method: GET/POST/HEAD等
cors: *
csrf: False
发送请求
使用python的Request包,不多说了
odoo定时任务
创建定时任务,也就是往ir.cron中增加记录,可以通过xml加载data或者界面上手动添加,是action的一种
主要参数
name:名称
interval_number: 间隔次数,数字类型,和间隔单位共同组成间隔时间,例如:3 days
interval_type: minutes, hours, days, weeks, months
numbercall:执行次数:-1表示无限执行
doall: 服务重启是否去做漏掉的数据
model_id:调用的方法的模型,模块.模型
code: 调用的方法,model.do_xxx()
odoo model三种继承
- _inherit = “A” 原模型继承,直接修改原表,共享方法和数据
- _inherit = “A” 且 _name = “B” 原型继承,相当于复制一份A表,重命名为B,并进行修改
- 给many2one字段加上 delegate=True 属性,委托继承,这个many2one字段进行增加时,会操作关联的表的数据
odoo view的继承
新建view,并且加上 inherit_id 属性指向继承的原视图,然后使用xpath定位元素进行修改。
注意:
ref指向的视图ID,格式为【模块名.视图ID】,这样当指向不同的模块的视图时才能找到,否则只会在当前模块下找
如果通过action去展示新的view,需要加上 view_id 属性,指向新的view的id,否则不会正常显示
odoo action
act_window:
可以在xml里定义绑定到菜单上,python方法里也可以返回,写好对应的参数,也会打开相应的页面
url_action
python方法中返回,打开设置的url地址
{
"type": "ir.actions.act_url",
"url": "https://odoo.com",
"target": "self", # target为new的话会打开新的页面
}
action_server
效果是在tree,form视图的动作按钮下拉框中增加功能
<record model="ir.actions.server" id="print_instance">
<field name="name">Res Partner Server Action</field>
<field name="model_id" ref="model_res_partner"/>
<field name="binding_model_id" ref="model_res_partner"/>
<field name="binding_view_types">list,form</field>
<field name="state">code</field>
<field name="code">
if record.some_condition():
action = {
"type": "ir.actions.act_window",
"view_mode": "form",
"res_model": record._name,
"res_id": record.id,
}
</field>
</record>
report_action
创建报告动作,下面和report一起写
ir_corn
定时任务,上面写过了
odoo report
odoo打印报表功能,自定义一个报告模板template,然后可以通过python方法来render这个模板,或者写个report_action来提供触发
<record id="print_portfolio_report" model="ir.actions.report">
<field name="model">portfolio</field> <!--绑定的模型-->
<field name="name">Portfolio Report</field>
<field name="report_name">practice.portfolio_report_template</field> <!--使用的报告模板-->
<field name="report_type">qweb-pdf</field>
<field name="binding_model_id" ref="model_portfolio"/> <!--报告这个action绑定到哪个模型上,这个模型的视图的动作按钮旁边会出现一个打印按钮-->
<field name="binding_type">report</field>
</record>
<template id="portfolio_report_template">
<t t-name="portfolio_report_template">
<t t-call="web.html_container"> <!--t-call继承别的模板-->
<t t-call="web.external_layout">
<div class="page"> <!--加一个page-->
<h2 align="center">
<span>作品集统计</span>
</h2>
<table class="table table-condensed table-bordered table-striped" style="text-align: center"
boder="1">
<thead>
<tr>
<th>名称</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr t-foreach="docs" t-as="line">
<td>
<span t-esc="line.name"/>
</td>
<td>
<span t-esc="line.description"/>
</td>
</tr>
</tbody>
</table>
</div>
</t>
</t>
</t>
</template>
以上xml不要忘了在mainifest中加载
如果要对数据做很多其他处理,需要写一个report模型,继承瞬态类,并实现_get_report_values方法,详细可参考odoo14生成PDF报告
odoo search视图
<record id="xxx_view_search" model="ir.ui.view">
<field name="name">xxx_view_search</field>
<field name="model">xxx.xxx</field>
<field name="arch" type="xml">
<search>
<field name="xxx"/>
<field name="xxx1"/>
<separator/>
<filter string="待办" name="todo" domain="[()]"/>
<searchpanel>
<field name="xxx" string="XXX" enable_counters="1"/>
</searchpanel>
<group expand="0" string="Group By">
<filter string="Xxx" name="group_by_xxx" domain="[]"
context="{'group_by': 'xxx_id'}"/>
</group>
</search>
</field>
</record>
field
field中添加的字段,将会在上面的搜索栏下拉框中显示,也就是你输入 123,会出现让你选择xxx=123或者xxx1=123的选项
filter
filer是写一个domain,将会增加在tree视图上方的筛选中,同时如果你希望进入tree视图默认有某个过滤条件,就可以写了filter之后,在act_window里传入context,格式:“search_default_过滤器的name”:1
<field name="search_view_id" ref="xxx_view_search"/>
<field name="context">{"search_default_todo":1}</field>
group
这个标签里分组内容,会出现在tree视图上方的分组下拉框中
增加filter,配上context=“{‘group_by’: ‘xxx_id’}”,会以xxx_id为依据对tree视图进行分组,如果想进入tree视图时默认分组,也是在act_widow里的context增加
<field name="search_view_id" ref="xxx_view_search"/>
<field name="context">{"search_default_group_by_xxx":1}</field>
searchpanel
这个标签里你增加的字段,将会作为分组依据,在tree视图左边划出一块区域做树状图,进行分层级,enable_counter 设为 1,会计算每个组的数目
odoo wizard
odoo 自定义widget
直接引用链接吧,感觉写的挺好 odoo自定义widget
基本思路就是写js,xml里导入js文件,视图里引用widget
钩子函数
写在模块的init文件中,在manifest中进行调用,有以下三种:
pre_init_hook:安装模块前执行
post_init_hook:安装模块后执行
uninstall_hook:卸载模块后执行
from odoo import api, SUPERUSER_ID, _
def uninstall_hook(cr, registry):
"""
数据初始化,卸载时执行
"""
env = api.Environment(cr, SUPERUSER_ID, {})
ir_config = env["ir.config_parameter"].sudo()
...
FASTAP
高性能的异步web框架
uvicorn:ASGI服务器,用来构建异步web服务,使用asyncio 库实现异步 I/O 操作
starlette:构建中间件,amount静态资源等
pydantic:构建数据结构,规范接口文档
fastapi:异步web服务框架,可以自动生成接口文档
大致思路如下
app = FastAPI(
docs_url="/docs",
on_startup=[database_connect, ],
on_shutdown=[database_disconnect, ]
)
def main():
uvicorn.run(app=app, host="0.0.0.0", port=8000)
MQTT
物联网传输协议,用于轻量级的实时消息发布/订阅消息传输
主要结构是发布者,代理和订阅者
基本使用:
- 启动mqtt server(broker)
- 建立mqtt client并进行pubulish和subscribe,推送和订阅不同的topic消息
不同的topic可以设定不同的qos
qos:0 表示只管发送不管是否重复和送到
qos:1 表示保证送到至少一次
qos:2 表示确保送到且只有一次
可设置的方法一般有on_connect,on_message,on_disconnect,设置回调函数,在触发这些动作的时候就会进行调用
多进程使用
通过multiprocessing包来实现,创建process对象,target调用方法,args传递参数,daemon设置是否为守护进程,守护进程随着主进程结束而结束,独立后台运行,一般实现web服务,数据库服务,定时清理之类的功能
Queue可以用来通信,在子进程中q.put(),在另一个进程中就可以q.get()
q = Queue()
p = Process(target=fun_test, args=(q,), daemon=True)
p.start()
p.join()
多线程使用
一般线程和进程很类似,调用threading库,也是target调用方法,args传参数,守护线程会在当前进程的所有非守护线程执行完毕之后挂掉
参考python多线程
t = threading.Thread(target=job, args=(data[i], q))
也可以使用线程池 ThreadPoolExecutor
先submit()提交任务到线程池,再使用as_complete()取出所有任务的结果,用task.result()来获取结果
with ThreadPoolExecutor(max_workers=8) as executor:
task_list = [executor.subumit(func,args) for i in range(10)]
for task in as_completed(task_list):
task_exception = task.exception()
if task_exception:
_logger.error("任务执行失败: %s", ustr(task_exception))
continue
data.append(task.result())
协程使用
通过async 和 await 来实现异步,await执行的方法必须是async方法,async方法中也必须有await调用
要运行协程需要创建一个event_loop对象,在主线程中直接get就可以,子线程就需要先set一个了。loop.run_until_complete表明会阻塞到coroutine执行完毕再结束
def async_main():
exec_list = [x for x in range(1, 6)]
task_list = [] # 任务列表
for x in exec_list:
task = asyncio.create_task(parseNode(x, x))
task_list.append(task) # 将每个任务都放入任务列表
for y in task_list:
await y # 使主线程等待每个任务执行完成
loop = asyncio.get_event_loop()
loop.add_signal_handler(signal.SIGINT, ask_exit)
loop.add_signal_handler(signal.SIGTERM, ask_exit)
loop.run_until_complete(async_main())
python 生成器和迭代器
用iter(obj)可以将一个可迭代对象变成迭代器,然后可以对这个迭代器进行next()调用
生成器是在函数中加入yield关键字,yield的作用是阻塞和return,直到next()调用这个生成器的时候,会再次执行到yield,说白了相当于打断点