操作

动作定义了系统对用户操作的响应行为:登录、操作按钮、选择发票等。

动作可以存储在数据库中,或者例如在按钮方法中直接以字典形式返回。所有动作共享两个必填属性:

type

当前动作的类别,决定可以使用哪些字段以及如何解释该动作

name

动作的简短用户可读描述,可能会显示在客户端界面中

客户端可以通过以下 4 种形式获取动作:

  • False

    如果当前有任何动作对话框打开,则关闭它

  • 字符串

    如果匹配到 客户端动作 ,则解释为客户端动作的标签,否则视为数字

  • 数字

    从数据库中读取相应的动作记录,可以是数据库标识符或 external id

  • 字典

    作为客户端动作描述符处理并执行

绑定

除了两个必填属性外,所有动作还共享一些用于在任意模型的上下文菜单中展示动作的 可选 属性:

binding_model_id

指定该动作绑定到哪个模型

注解

对于服务端动作,使用 model_id

binding_type

指定绑定类型,主要决定动作会出现在哪个上下文菜单下

action (默认值)

指定该动作将出现在绑定模型的 操作 上下文菜单中。

report

指定该动作将出现在绑定模型的 打印 上下文菜单中。

binding_view_types

一个以逗号分隔的视图类型列表,指定动作会出现在哪些视图类型的上下文菜单中,主要是 “list” 和/或 “form”。默认值为 list,form (即列表和表单)。

窗口动作 ( ir.actions.act_window )

最常见的动作类型,用于通过 视图 展示模型的可视化:窗口动作定义了一组视图类型(以及可能的具体视图),适用于某个模型(以及可能的特定记录)。

其字段包括:

res_model

要为其展示视图的模型

views

一个 (view_id, view_type) 对的列表。每对中的第二个元素是视图的类别(如列表、表单、图表等),第一个元素是一个可选的数据库 ID(或 False )。如果没有提供 ID,客户端应获取请求模型的指定类型的默认视图(这由 fields_view_get() 自动完成)。列表中的第一个类型是默认视图类型,在执行动作时会默认打开。每个视图类型在列表中最多出现一次。

res_id (可选)

如果默认视图为 form ,则指定要加载的记录(否则应创建新记录)

search_view_id (可选)

一个 (id, name) 对,其中 id 是为该动作加载的特定搜索视图的数据库标识符。默认情况下会获取模型的默认搜索视图。

target (可选)

视图应在主内容区域 ( current )、全屏模式 ( fullscreen ) 还是对话框/弹出窗口 ( new ) 中打开。使用 main 而不是 current 可清除面包屑导航。默认值为 current

context (可选)

传递给视图的额外上下文数据

domain (可选)

隐式添加到所有视图搜索查询中的过滤条件

limit (可选)

列表中默认显示的记录数。在 Web 客户端中默认为 80

例如,打开设置了 customer 标志的客户(合作伙伴),并使用列表和表单视图::

{
    "type": "ir.actions.act_window",
    "res_model": "res.partner",
    "views": [[False, "list"], [False, "form"]],
    "domain": [["customer", "=", true]],
}

或者在一个新对话框中打开特定产品(单独获取)的表单视图::

{
    "type": "ir.actions.act_window",
    "res_model": "product.product",
    "views": [[False, "form"]],
    "res_id": a_product_id,
    "target": "new",
}

数据库中的窗口动作有一些不同的字段,客户端应忽略这些字段,主要用于构建 views 列表:

view_mode (默认值= list,form

以逗号分隔的视图类型字符串(/!\ 不要包含空格 /!\)。所有这些类型都会出现在生成的 views 列表中(至少会有一个 False 的 view_id)

view_ids

多对多关系1 指向视图对象,定义了 views 的初始内容

注解

Act_window 视图也可以通过 ir.actions.act_window.view 清晰地定义。

如果计划为模型允许多个视图,建议使用 ir.actions.act_window.view 而不是动作的 view_ids

<record model="ir.actions.act_window.view" id="test_action_tree">
   <field name="sequence" eval="1"/>
   <field name="view_mode">list</field>
   <field name="view_id" ref="view_test_tree"/>
   <field name="act_window_id" ref="test_action"/>
</record>
view_id

如果该视图的类型属于 view_mode 列表且未被 view_ids 中的某个视图填充,则将其作为特定视图添加到 views 列表中

这些主要在从 数据文件(Data Files) 定义动作时使用:

<record model="ir.actions.act_window" id="test_action">
    <field name="name">A Test Action</field>
    <field name="res_model">some.model</field>
    <field name="view_mode">graph</field>
    <field name="view_id" ref="my_specific_view"/>
</record>

即使这不是模型的默认视图,也会使用 “my_specific_view” 视图。

服务器端 views 序列的组成如下:

  • view_ids 中获取每个 (id, type)``(按 ``sequence 排序)

  • 如果定义了 view_id 且其类型尚未填充,则追加其 (id, type)

  • for each unfilled type in view_mode, append (False, type)

1

技术上不是多对多关系:增加了序列字段,并且可以仅由视图类型组成,而没有视图 ID。

URL 动作 (ir.actions.act_url)

允许通过 Odoo 动作打开 URL(网站/网页)。可以通过两个字段进行自定义:

url

激活动作时要打开的地址

target (默认值= new

可用的值是:

  • new :在新窗口/页面中打开 URL

  • self :在当前窗口/页面中打开 URL(替换实际内容)

  • download :重定向到下载 URL

示例:

{
    "type": "ir.actions.act_url",
    "url": "https://odoo.com",
    "target": "self",
}

这将用 Odoo 主页替换当前内容部分。

服务端动作 ( ir.actions.server )

class odoo.addons.base.models.ir_actions.IrActionsServer(env: Environment, ids: tuple[IdType, ...], prefetch_ids: Reversible[IdType])[源代码]

服务器动作模型。服务器动作作用于基础模型,并提供多种类型的动作,可以自动执行(例如使用基础动作规则),也可以手动执行(通过在“更多”上下文菜单中添加动作)。

自 Odoo 8.0 起,在动作表单视图中提供了一个“创建菜单动作”按钮。它会在基础模型的“更多”菜单中创建一个条目。这使得可以通过界面轻松创建服务器动作并以批量模式运行它们。

可用的动作包括:

  • ‘执行 Python 代码’:将执行一段 Python 代码块

  • ‘创建新记录’:使用新值创建一条记录

  • ‘写入记录’:更新记录的值

  • ‘执行多个动作’:定义一个触发其他多个服务器动作的动作

允许从任何有效的动作位置触发复杂的服务端代码。仅有两个字段与客户端相关:

id

要运行的服务端动作的数据库标识符

context (可选)

运行服务端动作时使用的上下文数据

数据库中的记录功能更丰富,可以根据其 state 执行一些特定或通用的动作。某些字段(及相应的行为)在不同状态之间共享:

model_id

与动作关联的 Odoo 模型。

state

  • code :通过 code 参数执行 Python 代码。

  • object_create :根据 fields_lines 规范创建模型 crud_model_id 的新记录。

  • object_write :根据 fields_lines 规范更新当前记录。

  • multi :通过 child_ids 参数执行多个动作。

状态字段

根据其状态,行为通过不同的字段定义。相关状态在每个字段后给出。

code (代码)

指定在调用动作时执行的一段 Python 代码

<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="state">code</field>
    <field name="code">
        raise Warning(record.name)
    </field>
</record>

注解

代码段可以定义一个名为 action 的变量,该变量将作为下一个要执行的动作返回给客户端:

<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="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>

如果记录满足某些条件,将要求客户端为其打开表单

crud_model_id (创建)(必填)

要在其中创建新记录的模型

link_field_id (创建)

多对一关系到 ir.model.fields ,指定应在当前记录的 m2o 字段上设置新创建的记录(模型应匹配)

fields_lines (创建/写入)

在创建或复制记录时需要覆盖的字段。 One2many 包含以下字段:

col1

要在相关模型中设置的 ir.model.fields (对于创建操作是 crud_model_id ,对于更新操作是 model_id

value

字段的值,通过 type 解释

type (值|引用|表达式)

如果是 value ,则 value 字段被解释为字面值(可能经过转换);如果是 equation ,则 value 字段被解释为 Python 表达式并进行求值

child_ids (多动作)

指定在多动作状态下执行的多个子动作( ir.actions.server )。如果子动作本身返回动作,则最后一个动作将作为多动作的下一个动作返回给客户端

求值上下文

在服务端动作或其周围的求值上下文中,有多个键可用:

  • 通过 model_id 链接到动作的模型对象 model

  • 触发动作的记录/记录集 record/records ,可以为空。

  • env Odoo 环境

  • 对应的 Python 模块 datetimedateutiltimetimezone

  • log: log(message, level='info') 日志函数,用于在 ir.logging 表中记录调试信息

  • 用于 Warning 异常的 Warning 构造函数

报告动作 ( ir.actions.report )

触发报告的打印。

如果您通过 <record> 而不是 <report> 标签定义报告,并希望该动作出现在模型视图的“打印”菜单中,还需要指定 绑定 中的 binding_model_id 。无需将 binding_type 设置为 report ,因为 ir.actions.report 会隐式默认为该值。

name (必填)

如果未指定 print_report_name ,则用作文件名。否则,仅在某种列表中查找报告时作为助记符/描述有用。

model (必填)

报告所涉及的模型

report_type (默认值=qweb-pdf)

对于 PDF 报告使用 qweb-pdf ,对于 HTML 报告使用 qweb-html

report_name (必填)

用于渲染报告的 qweb 模板名称( external id

print_report_name

定义报告名称的 Python 表达式。

groups_id

Many2many 字段,指向允许查看/使用当前报告的用户组

multi

如果设置为 True ,该动作将不会显示在表单视图中。

paperformat_id

Many2one 字段,指向您希望用于此报告的纸张格式(如果未指定,则使用公司格式)

attachment_use

如果设置为 True ,报告仅在首次请求时生成一次,之后从存储的报告中重新打印,而不是每次重新生成。

可用于只需生成一次的报告(例如出于法律原因)。

attachment

定义报告名称的 Python 表达式;记录可通过变量 object 访问

客户端动作 ( ir.actions.client )

触发完全在客户端实现的动作。

tag

动作的客户端标识符,客户端应知道如何响应的任意字符串

params (可选)

一个 Python 字典,包含要与客户端动作标签一起发送到客户端的附加数据

target (可选)

客户端动作应在主内容区域 ( current )、全屏模式 ( fullscreen ) 还是对话框/弹出窗口 ( new ) 中打开。使用 main 而不是 current 可清除面包屑导航。默认值为 current

{
    "type": "ir.actions.client",
    "tag": "pos.ui"
}

告诉客户端启动销售点界面,服务器并不了解 POS 界面的工作方式。

计划动作 ( ir.cron )

按照预定义频率自动触发的动作。

name

计划动作的名称(主要用于日志显示)

interval_number

两次动作执行之间的 interval_type 单位数量

interval_type

频率间隔的单位( 分钟小时

model_id

将调用此动作的模型

code

动作的代码内容。可以是对模型方法的简单调用:

model.<method_name>()
nextcall

此动作的下次计划执行日期(日期/时间格式)

priority

同时执行多个动作时该动作的优先级

编写定时任务函数

运行计划操作时,建议尝试批量处理进度,以避免长时间阻塞工作进程并可能遇到超时异常。因此,您应该拆分处理过程,以便每次调用都能在部分待完成的工作上取得进展。

编写此类函数时,应专注于处理单个批次。一个批次应处理一个或多个记录,并且通常耗时不应超过 几秒钟

框架在每个批次后提交工作。框架将根据需要多次调用该函数以处理剩余工作。请勿自行重新调度作业。

IrCron._commit_progress(processed: int = 0, *, remaining: int | None = None, deactivate: bool = False) float[源代码]

从 cron 函数提交并记录批次的进度。

已处理的项目数量会添加到当前完成计数中。如果未指定剩余计数,则从现有剩余计数中减去已处理的项目数量。

如果从 cron 作业外部调用,进度函数调用将仅执行提交。

参数
  • processed – 此步骤中已处理的项目数量

  • remaining – 将剩余计数设置为给定计数

  • deactivate – 运行后停用该 cron 作业

返回

cron 运行的剩余时间(秒)

def _cron_do_something(self, *, limit=300):  # limit: allows for tweaking
    domain = [('state', '=', 'ready')]
    records = self.search(domain, limit=limit)
    records.do_something()
    # notify progression
    remaining = 0 if len(records) == limit else self.search_count(domain)
    self.env['ir.cron']._commit_progress(len(records), remaining=remaining)

在某些情况下,您可能希望在多个批次之间共享资源或自行管理循环以处理异常。在这种情况下,您应通过调用 IrCron._commit_progress() 并检查结果来通知调度程序您的工作进度。进度函数返回调用的剩余秒数;如果为 0,您必须尽快返回。

以下是一个示例,展示如何在处理每条记录后提交,同时保持连接打开。

def _cron_do_something(self):
    assert self.env.context.get('cron_id'), "Run only inside cron jobs"
    domain = [('state', '=', 'ready')]
    records = self.search(domain)
    self.env['ir.cron']._commit_progress(remaining=len(records))

    with open_some_connection() as conn:
        for record in records:
            # You may have other needs; we do some common stuff here:
            # - lock record (also checks existence)
            # - prefetch: break prefetch in this case, we process one record
            # - filtered_domain: record may have changed
            record = record.try_lock_for_update().filtered_domain(domain)
            if not record:
                continue
            # Processing the batch here...
            try
                record.do_something(conn)
                if not self.env['ir.cron']._commit_progress(1):
                    break
            except Exception:
                # if you handle exceptions, the default stategy is to
                # rollback first the error
                self.env.cr.rollback()
                _logger.warning(...)
                # you may commit some status using _commit_progress

运行 cron 函数

您不应直接调用 cron 函数。有两种运行函数的方式:

IrCron.method_direct_trigger()[源代码]

在当前(HTTP)线程中运行 CRON 作业。

作业仍按调度程序的方式运行:使用新的游标执行作业。

引发

UserError – 当作业已在运行时

IrCron._trigger(at: datetime | Iterable[datetime] | None = None)[源代码]

安排一个 cron 作业尽快执行,与其 nextcall 字段值无关。

默认情况下,cron 被安排在下一次 cron 工作进程唤醒时执行,但可选的 at 参数可用于延迟执行,精度低至 1 分钟。

该方法可以使用 datetime 或 datetime 的可迭代对象调用。实际实现在 _trigger_list() 中,这是推荐的覆盖方法。

参数

at – 何时执行 cron,在一个或多个时间点而不是尽快执行。

返回

创建的触发器记录

cron 函数的测试应通过在注册表测试模式下调用 IrCron.method_direct_trigger() 来完成。

安全性

为避免计划动作之间资源的公平使用问题,一些安全措施确保您的计划动作能够正确运行。

  • 如果某个计划动作连续三次遇到错误或超时,它将跳过当前执行并被视为失败。

  • 如果某个计划动作在至少七天内连续五次执行失败,它将被停用,并通知数据库管理员。

  • 在数据库级别存在 cron 执行的硬限制,超过该限制后,执行 cron 作业的进程将被终止。