代码速查表
后端
Domain
from odoo.osv import expression
- expression.AND(domains)
- expression.OR(domains)
One2many/Many2many Command
对于x2m的关联关系,通过更直观的命名函数执行更具有可读性,以此替代三元组首位为整数的方式。
from odoo import Command
or from odoo.fields import Command
-
Command.create(values)
: 传入values, 创建新的记录并关联到当前记录。 -
Command.update(id, values)
: 传入id、values,修改关联记录的数据。 -
Command.delete(id)
: 传入id,移除关联关系。 -
Command.unlink(id)
: 传入id,移除关联关系并删除指定id的记录集。 -
Command.link(id)
: 传入id,添加关联关系。 -
Command.clear()
: 移除所有关联关系。 -
Command.set(ids)
: 传入ids,先移除所有关联关系,再将所有ids添加关联。
return ir.actions.client
- display_notification(右上角提示窗)
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'type': 'success', # 提示窗类型,可选项: success/danger/warning/info/...
'title': _("Leads Assigned"), # 标题
'message': 'Message, %s',
'next': { # 在services.notification.add之后返回next的action
'type': 'ir.actions.act_window_close'
},
'links': [{ # 在message中通过%s占位符设置一个a标签
'label': production.name,
'url': f'#action={action.id}&id={production.id}&model=mrp.production'
}],
'sticky': True, # 是否滞留提示窗,需要手工关闭。
}
}
通过/web/export/路由快速实现导出excel
摘自odoo17,其他版本待核实。
import json
from urllib.parse import urlencode
def export_xlsx(self):
params = {
"data": json.dumps({
"domain": domain,
"fields": export_fields, # 导出的列会按照这个列表的顺序
"groupby": [],
"ids": False,
"import_compat": False,
"model": "sale.order",
})
}
params_json = json.dumps(params)
return {
"name": "Sale Order",
"type": "ir.actions.act_url",
"url": '/web/export/xlsx?%s' % urlencode(params),
"target": "new",
}
将many2many(ir.attachment)打包成zip
import zipfile
import io
import base64
class YourModel(models.Model):
def pack_attachments_to_zip(self, attachment_ids,
zip_filename="attachments.zip"):
"""
将多个附件打包成zip格式并存储为新的附件
:param attachment_ids: many2many附件的ID列表
:param zip_filename: 生成的zip文件名
:return: 新创建的zip附件记录
"""
# - 使用内存缓冲区避免创建临时文件
# - 支持自定义zip文件名
# - 保持原始文件名在zip包内
# - 返回新创建的ir.attachment记录
if not attachment_ids:
return False
if isinstance(attachment_ids, list):
attachment_ids = self.env['ir.attachment'].browse(attachment_ids)
if isinstance(attachment_ids, int):
attachment_ids = self.env['ir.attachment'].browse([attachment_ids])
# 创建内存中的zip文件
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
# 遍历所有附件
for attachment in attachment_ids:
if attachment.datas:
# 解码附件数据
file_data = base64.b64decode(attachment.datas)
# 添加到zip文件中,使用原始文件名
zip_file.writestr(attachment.name or f"file_{attachment.id}",
file_data)
# 获取zip文件的二进制数据
zip_buffer.seek(0)
zip_data = zip_buffer.getvalue()
zip_buffer.close()
# 编码为base64
zip_base64 = base64.b64encode(zip_data)
# 创建新的附件记录
zip_attachment = self.env['ir.attachment'].create({
'name': zip_filename,
'type': 'binary',
'datas': zip_base64,
'mimetype': 'application/zip',
'res_model': self._name,
'res_id': self.id,
})
return zip_attachment
使用示例
# 在模型中调用
class YourModel(models.Model):
_name = 'your.model'
attachment_ids = fields.Many2many('ir.attachment', string='附件')
zip_attachment_id = fields.Many2one('ir.attachment', string='压缩包附件')
def create_zip_attachment(self):
if self.attachment_ids:
zip_attachment = self.pack_attachments_to_zip(
self.attachment_ids.ids,
f"attachments_{self.id}.zip"
)
self.zip_attachment_id = zip_attachment.id
return zip_attachment
return False
通过_auto_init/init新建索引(index)
使用示例: 以下是原生代码在hh_leave表上新增一条索引。此处用到odoo.tools.sql.craete_index(可以通过from odoo.tools import create_index
引用)。
def _auto_init(self):
res = super(HolidaysRequest, self)._auto_init()
tools.create_index(self._cr, 'hr_leave_date_to_date_from_index',
self._table, ['date_to', 'date_from'])
return res
官方更推荐将创建索引的操作放在init
函数中,因为init
函数会在_auto_init
之后被调用。
_auto_init
的说明
_auto_init
的说明Initialize the database schema of self
:
- create the corresponding table,
- create/update the necessary columns/tables for fields,
- initialize new columns on existing rows,
- add the SQL constraints given on the model,
- add the indexes on indexed fields,
Also prepare post-init stuff to:
- add foreign key constraints,
- reflect models, fields, relations and constraints,
- mark fields to recompute on existing records.
Note: you should not override this method. Instead, you can modify
the model's database schema by overriding method :meth:~.init
,
which is called right after this one.
create_index
源码
def create_index(cr, indexname, tablename, expressions, method='btree', where=''):
""" Create the given index unless it exists. """
if index_exists(cr, indexname):
return
args = ', '.join(expressions)
if where:
where = f' WHERE {where}'
cr.execute(f'CREATE INDEX "{indexname}" ON "{tablename}" USING {method} ({args}){where}')
_schema.debug("Table %r: created index %r (%s)", tablename, indexname, args)
前端
Search View - Filter 筛选日期周期
<filter string="当天" name="today" domain="[('create_date','>=', time.strftime('%Y-%m-%d 00:00:00')),('create_date', '<', context_today().strftime('%Y-%m-%d 23:59:59'))]"/>
<filter string="本周" name="last_week" domain="[('create_date','>', (context_today() - datetime.timedelta(weeks=1)).strftime('%%Y-%%m-%%d 00:00:00'))]"/>
<filter string="本月" name="month" domain="[('create_date','>=', time.strftime('%Y-%m-01 00:00:00')),('create_date','<', (context_today() + relativedelta(months=1)).strftime('%Y-%m-01 00:00:00'))]"/>
<filter string="上月" name="month2" domain="[('create_date','<', time.strftime('%Y-%m-01 00:00:00')),('create_date','>=', (context_today() - relativedelta(months=1)).strftime('%Y-%m-01 00:00:00'))]"/>
<filter string="本年" name="year" domain="[('create_date','<=', time.strftime('%Y-12-31 23:59:59')),('create_date','>=', time.strftime('%Y-01-01 00:00:00'))]"/>
<separator/>
<filter name="过去24小时" string="Last 24h" domain="[('create_date','>', (context_today() - datetime.timedelta(days=1)).strftime('%Y-%m-%d 00:00:00') )]"/>
<filter name="上周" string="Last Week" domain="[('create_date','>', (context_today() - datetime.timedelta(weeks=1)).strftime('%Y-%m-%d 00:00:00'))]"/>
<!--另一种写法-->
<separator/>
<filter name="week" string="本周"
domain="[
'&',
('create_date', '>=', (context_today() + relativedelta(weeks=-1,days=1,weekday=0)).strftime('%Y-%m-%d')),
('create_date', '<=', (context_today() + relativedelta(weekday=6)).strftime('%Y-%m-%d')),
]"/>
<filter name="month" string="本月"
domain="[
'&',
('create_date', '>=', (context_today() + relativedelta(day=1)).strftime('%Y-%m-%d')),
('create_date', '<=', (context_today() + relativedelta(months=1, day=1, days=-1)).strftime('%Y-%m-%d')),
]"/>
Search View - Searchpanel
<searchpanel>
<field name="category_id" limit="20" select="one" icon="fa-list-ul" enable_counters="1" hierarchize="1"/>
<field name="tag_ids" limit="20" select="multi" icon="fa-tags" enable_counters="1"/>
</searchpanel>
一些html/css/js
- 转圈icon:
<i class="fa fa-spinner fa-spin"/>