升级工具¶
升级工具 是一个包含辅助函数的库,用于简化升级脚本的编写。该库被 Odoo 用于标准模块的升级脚本,提供了可靠性并有助于加快升级过程:
辅助函数帮助确保数据库中的数据一致性。
它处理更新记录的间接引用。
允许调用函数以避免编写代码,节省时间并降低开发风险。
辅助工具使您可以专注于升级的重要部分,而无需考虑细节。
安装¶
克隆 升级工具仓库 到本地,并在启动 odoo 时将 src 目录添加到 --upgrade-path 选项之前。
$ ./odoo-bin --upgrade-path=/path/to/upgrade-util/src,/path/to/other/upgrade/script/directory [...]
在您不自行管理 Odoo 的平台上,可以通过 pip 安装此库:
$ python3 -m pip install git+https://github.com/odoo/upgrade-util@master
在 Odoo.sh 上,建议将其添加到自定义仓库的 requirements.txt 文件中。为此,请在文件中添加以下行:
odoo_upgrade @ git+https://github.com/odoo/upgrade-util@master
使用升级工具¶
安装完成后,以下包可供升级脚本使用:
odoo.upgrade.util: 辅助工具本身。odoo.upgrade.testing: 基础 TestCase 类。
要在升级脚本中使用它,只需导入即可:
from odoo.upgrade import util
def migrate(cr, version):
# Rest of the script
现在,可以通过 util 调用辅助函数。
工具函数¶
升级工具提供了许多有用的函数来简化升级过程。在此,我们将描述一些最有用的函数。有关辅助函数的完整声明,请参阅 util 文件夹 。
注解
工具函数中的 cr 参数始终指代数据库游标。传递在 migrate() 中接收到的游标。并非所有函数都需要此参数。
模块¶
用于模块级操作的实用函数。
在大多数情况下,模块操作(重命名、合并、删除等)应在 base 脚本中执行。原因是一旦 base 模块升级完成,所有与模块相关的信息应该已经设置在数据库中,以确保升级过程正常运行。命令行选项 --pre-upgrade-scripts (从 Odoo 16 开始可用)允许在加载 base 模块之前运行升级脚本。这是在主要升级后执行模块操作的推荐方式。
- module_installed(cr, module)[源代码]¶
返回模块是否已安装。
参见
modules_installed()。
- uninstall_theme(cr, theme, base_theme=None)[源代码]¶
卸载主题模块并将其从网站中移除。
警告
此函数只能在
website模块的post-脚本中使用,因为它依赖于 ORM。参见
remove_theme()和uninstall_module()。
- remove_module(cr, module)[源代码]¶
完全删除模块。
此操作等同于卸载并移除模块的所有引用——数据库中不会留下任何痕迹。
- 参数
module (str) – 要移除的模块名称
警告
由于此函数会移除与模块关联的所有数据,请在调用此函数之前确保重新分配记录。
- remove_theme(cr, theme, base_theme=None)[源代码]¶
卸载主题模块。
警告
此函数只能在
post-脚本中使用。参见
remove_module()和uninstall_theme()。
- merge_module(cr, old, into, update_dependers=True)[源代码]¶
将一个模块合并到另一个模块中。
此函数将源模块中的所有引用和记录移动到目标模块中。
警告
此函数不会删除任何记录,但会从源模块中移除与目标模块中名称冲突的 xml_ids。
- force_install_module(cr, module, if_installed=None, reason='it has been explicitly asked for')[源代码]¶
强制 ORM 安装模块。
- force_upgrade_of_fresh_module(cr, module, init=True)[源代码]¶
强制执行正在安装的模块的升级脚本。
标准 Odoo 在安装模块时不运行升级脚本。这在技术上是有意义的,因为模块并未真正升级。然而,有时(新)模块需要执行一些操作才能正确安装,例如从其他模块获取数据。这种情况在模块功能被 拆分 为多个模块时很常见。
处于 init 模式的一个副作用是不遵守 XML 文件或
ir_model_data中的 noupdate 标志。
模型¶
用于修改模型的实用函数。
模型操作最好在相关模块的 pre- 脚本中完成。
- remove_model(cr, model, drop_table=True, ignore_m2m=())[源代码]¶
从数据库中删除模型及其引用。
某些对模型的必要间接引用会被替换为 未知模型 —— 一个作为空占位符的空模型,用于替代已移除的模型。
- delete_model(cr, model, drop_table=True, ignore_m2m=())[源代码]¶
从数据库中删除模型及其引用。
某些对模型的必要间接引用会被替换为 未知模型 —— 一个作为空占位符的空模型,用于替代已移除的模型。
- rename_model(cr, old, new, rename_table=True, ignored_m2ms='ALL_BEFORE_18_1')[源代码]¶
重命名模型。
更新数据库中所有对模型名称的引用。
如果请求重命名表,从 saas~18.1+ 开始,m2m 表也会更新,除非被忽略。在旧版本中,除非传递空列表,否则会跳过 m2m 表。
- merge_model(cr, source, target, drop_table=True, fields_mapping=None, ignore_m2m=())[源代码]¶
将一个模型合并到另一个模型中。
此函数将所有引用从
source模型移动到target模型,并 删除source模型及其引用。默认情况下,仅移动两个模型中同名的字段;可选地,可以提供不同字段名称的映射。警告
此函数不会将记录从
source模型移动到target模型。
- remove_inherit_from_model(cr, model, inherit, keep=(), skip_inherit=(), with_inherit_parents=True)[源代码]¶
从
model中移除inherit。此函数会从
model及其所有子模型中移除通过inherit继承的所有字段。包括继承模型及其父模型中的所有字段也将被移除,除非未设置with_inherit_parents模式。在这种情况下,仅移除inherit中的字段,而保留其父模型中的字段。在keep中列出的字段永远不会被移除。如果model的某些子模型列在skip_inherit中,它们将保留来自inherit的字段。
字段¶
用于修改模型字段的实用函数。
字段操作最好在相关模块的 pre- 脚本中完成。在某些情况下,初步操作可以在 pre 中完成,然后在 post 中完成。一个常见的例子是在 pre- 中移除字段但保留其列,稍后在 post 中当列最终被删除时使用。
- remove_field(cr, model, fieldname, cascade=False, drop_column=True, skip_inherit=(), keep_as_attachments=False)[源代码]¶
从数据库中移除字段及其引用。
此函数还会从继承模型中移除字段,除非在
skip_inherit中指定了例外情况。当字段已存储时,我们可以选择不删除该列。
- move_field_to_module(cr, model, fieldname, old_module, new_module, skip_inherit=())[源代码]¶
将字段从一个模块移动到另一个模块。
此函数更新对特定字段的所有引用,从源模块切换到目标模块。它避免了在注册表完全加载后数据丢失。继承模型中的字段也会被移动,除非跳过。
- rename_field(cr, model, old, new, update_references=True, domain_adapter=None, skip_inherit=())[源代码]¶
在给定的
model上将字段及其引用从old重命名为new。该字段会在所有继承模型中更新,但
skip_inherit中指定的模型除外。此函数还会更新直接或间接的引用,包括过滤器、服务器操作、相关字段、电子邮件、仪表板、域等更多内容。参见
update_field_usage()可以使用特殊的适配器函数来更新域。默认适配器仅在每个域叶中将
old替换为new。有关域适配器的信息,请参阅adapt_domains()。
- convert_m2o_field_to_m2m(cr, model, field, new_name=None, m2m_table=None, col1=None, col2=None)[源代码]¶
将 Many2one 字段转换为 Many2many。
它会创建关系表并填充它。
- m2o2m2m(cr, model, field, new_name=None, m2m_table=None, col1=None, col2=None)[源代码]¶
将 Many2one 字段转换为 Many2many。
它会创建关系表并填充它。
- change_field_selection_values(cr, model, field, mapping, skip_inherit=())[源代码]¶
替换选择字段值的引用。
此函数根据映射替换对选择值的所有引用。域也会被更新。
- update_field_usage(cr, model, old, new, domain_adapter=None, skip_inherit=())[源代码]¶
在不同位置将字段
old的所有引用替换为new。- 搜索范围:
ir_filters
ir_exports_line
ir_act_server
mail_alias
ir_ui_view_custom(仪表板)
域(使用
domain_adapter)相关字段
此函数可用于替换字段的使用。域会使用
domain_adapter进行更新。默认情况下,域适配器仅在域叶中将old替换为new。有关域适配器的更多信息,请参阅adapt_domains()。
记录¶
用于记录级操作的实用函数。
- remove_view(cr, xml_id=None, view_id=None, silent=False, key=None)[源代码]¶
删除视图及其所有子视图。
此函数递归删除给定视图及其继承的视图,只要它们是模块的一部分。如果层次结构中存在任何自定义视图,该函数将失败。它还会删除多网站的 COW 视图。
- 参数
警告
必须设置
xml_id或view_id。同时指定两者将引发错误。
- edit_view(cr, xmlid=None, view_id=None, skip_if_not_noupdate=True, active='auto')[源代码]¶
用于编辑视图架构的上下文管理器。
此函数返回一个上下文管理器,可能会生成视图的解析架构,作为 etree 元素 。返回对象中的任何更改将在退出上下文管理器时写回数据库,并更新架构的翻译版本。由于函数可能不会生成内容,可以使用
skippable_cm()来避免错误。with util.skippable_cm(), util.edit_view(cr, "xml.id") as arch: arch.attrib["string"] = "My Form"
To select the target view to edit use either
xmlidorview_id, not both.当通过
view_id确定视图时,如果视图存在,则始终生成其架构,而不考虑其可能关联的noupdate标志。当设置了xmlid时,如果视图的noupdate标志为False,则不会生成架构,除非将skip_if_not_noupdate设置为False。如果noupdate为True,则生成视图以供编辑。如果
active参数为True或False,则视图的active标志将相应设置。注解
如果
active为 “auto”(默认值),则通过xmlid选择时视图将被激活,而通过view_id选择时保持不变。
- is_changed(cr, xmlid, interval='1 minute')[源代码]¶
返回记录是否已更改。
此函数检查记录是否在当前升级开始时间之前已更改。参见
upgrade-util/src/base/0.0.0/pre-00-upgrade-start.py对于符合以下条件的记录的 xmlid,此工具将返回假阳性:
已在当前升级之前的升级中更新过
尚未在当前升级中更新
如果
xmlid不存在于数据库中,此函数返回None。
- if_unchanged(cr, xmlid, callback, interval='1 minute', **kwargs)[源代码]¶
如果记录未更改,则运行
callback。当引用的记录未更改时,此函数将运行
callback。xmlid和任何其他参数(但不包括interval)将传递给callback。如果记录已更改,则会将其标记为noupdate=True。另请参见is_changed()和force_noupdate()。此函数在记录未更新时执行操作非常有用,一个常见示例是即使记录为
noupdate=True,也强制从 XML 更新。util.if_unchanged(cr, "mymodule.myrecord", util.update_record_from_xml)
- rename_xmlid(cr, old, new, noupdate=None, on_collision='fail')[源代码]¶
重命名记录的 external identifier (
xml_id)。如果目标名称已存在于数据库中,则无法进行重命名。在这种情况下,有两种选项可以控制此函数的行为:
fail:引发异常并阻止重命名merge:重命名外部标识符,删除旧标识符,并替换引用。参见replace_record_references_batch()
注解
此函数不会删除记录,仅更新 xml_ids。
- ensure_xmlid_match_record(cr, xmlid, model, values)[源代码]¶
确保 xml_id 引用具有特定值的记录。
此函数确保 xml_id 指向的记录与
values参数中指定字段的值匹配。如果 xmlid 存在,但它指向的记录与值不匹配,则会将 xmlid 更新为指向一个匹配的记录(如果找到)。如果 xmlid 不存在,则使用找到的记录创建它。如果未找到匹配的记录,则不会执行任何操作。在所有情况下,都会返回 xml_id 引用的记录(可能已更新)。- 参数
- 返回
匹配记录的 ID,如果未找到记录则为
None- 返回类型
小技巧
在将数据库中的记录迁移到自定义模块时,此函数非常有用,可以在模块更新之前创建 xml_ids 并避免重复。
- update_record_from_xml(cr, xmlid, reset_write_metadata=True, force_create=AUTO, from_module=None, reset_translations=(), ensure_references=False, fields=None)[源代码]¶
根据其在 数据文件(Data Files) 中的定义更新记录。
此函数忽略记录的
noupdate标志。它会在 xmlid 或from_module参数(如果设置)所指定的模块清单中的所有 XML 文件中搜索匹配的定义。找到后,它会强制 ORM 按照数据文件中的规范更新记录。可选地,此函数可以重置某些字段的翻译。
- 参数
xmlid (str) – 记录的 xml_id,格式为
module.namereset_write_metadata (bool) – 是否更新记录的
write_dateforce_create (bool) – 如果记录不存在是否创建记录。默认为
True,除非fields不为 None。from_module (str) – 从中更新记录的模块名称,仅当规范位于不同于 xml_id 所在模块的其他模块中时才需要。
ensure_references (bool) – 通过
refXML 属性引用的记录是否也应更新。fields (set(str) or None) – 要在 XML 声明中包含的字段的可选列表。如果设置,所有其他字段将被忽略。如果设置且记录缺失,则不会创建记录。
警告
此函数使用 ORM,因此只能在记录的数据规范中引用的所有模型均已加载之后使用。实际上,这意味着此函数应在
post-或end-脚本中使用。注解
ORM 的标准行为是如果记录不存在则创建记录,包括其 xml_id。此函数也会发生这种情况,除非将
force_create设置为False。
- delete_unused(cr, *xmlids, **kwargs)[源代码]¶
删除未使用的记录。
此函数仅在
xmlids指向的记录未被任何表引用时才会删除这些记录。对于具有层级结构的记录(例如产品分类),它会检查标记为级联删除的子记录是否也未被引用。在这种情况下,记录及其子记录将全部删除。注解
无法删除的记录将被设置为
noupdate=True。
- replace_record_references_batch(cr, id_mapping, model_src, model_dst=None, replace_xmlid=True, ignores=(), parent_field='parent_id')[源代码]¶
替换对记录的所有引用。
此函数将对
model_src记录的所有引用(直接或间接)替换为映射中的对应记录。如果映射的目标模型与源模型不同,则必须设置model_dst参数。
ORM¶
通过 ORM 执行操作的实用函数。
此模块中的函数允许在升级期间安全地使用 ORM。它们增强或修补了 ORM,使其能够高效处理大量数据。在某些情况下,提供了完全不同于 ORM 自身功能的替代方案。此模块中的函数适用于 所有 支持版本的 ORM。
- env(cr)[源代码]¶
创建一个新环境。
警告
此函数不会清空为超级用户维护的缓存(在空环境中)。每次直接在数据库中修改数据时,可能需要调用
invalidate_cache。- 返回
新环境
- 返回类型
- recompute_fields(cr, model, fields, ids=None, logger=<Logger odoo.upgrade.util.orm (WARNING)>, chunk_size=256, strategy='auto', query=None)[源代码]¶
重新计算字段值。
此函数将重新计算模型的字段值,限制为一组记录或所有记录。重新计算不会一次性处理所有记录,而是分成多个批次(块)。这样可以避免性能问题,并在最坏情况下避免因
MemoryError导致失败。每个块处理完成后,数据会根据以下策略之一返回数据库:flush :使用 ORM 的
flush方法commit :提交游标(同时也会刷新)
auto :根据要计算的记录数量和是否存在跟踪字段,从上述两种策略中选择最佳选项。
commit 策略在处理大量数据时更不容易导致
MemoryError。- 参数
- class iter_browse(model, *args, **kw)[源代码]¶
遍历记录集。
此类返回的
callable对象可以用作迭代器,按块加载记录(到recordset中)。每个块耗尽后,其数据会被发送回数据库(刷新或提交),然后加载下一个块。此类允许运行如下代码:
for record in env['my.model'].browse(ids): record.field1 = val env['my.model'].browse(ids)._compute_field2() env['my.model'].create(values)
以高效的方式运行,同时避免
MemoryError,即使ids或values包含数百万条目。使用此类的替代方法是:Example
MyModel = util.env(cr)['my.model'] for record in util.iter_browse(MyModel, ids): record.field1 = val util.iter_browse(MyModel, ids)._compute_field2() util.iter_browse(MyModel, ids).create(values)
- 参数
model (
odoo.model.Model) – 要迭代的模型chunk_size (int) – 每次迭代块中加载的记录数量,默认为
200logger (
logging.Logger) – 用于报告进度的日志记录器,默认为_loggerstrategy (str) – 是否在每个块上执行
flush或commit,默认为flush
- 返回
此类返回的对象可用于安全地迭代或调用任何模型方法,即使是数百万条记录。
另请参见
env()
- normalize_domain(domain)[源代码]¶
返回域的规范化版本。
规范化版本将所有隐式的 ‘&’ 运算符显式化。规范化域表达式的一个特性是它们可以轻松组合在一起,就好像它们是单个域组件一样。
- adapt_domains(cr, model, old, new, adapter=None, skip_inherit=(), force_adapt=False)[源代码]¶
在域中使用
model和继承模型将old替换为new。adapter是一个用于调整叶子节点的回调函数。适配器函数必须接受三个参数,并返回一个替换原始叶子节点的 域 。参数如下:leaf:一个域叶子节点,形式为(left, op, right)的元组in_or:布尔值,当为True时表示该叶子节点是 OR ("|") 域的一部分,否则是 AND ("&") 域的一部分negated:布尔值,当为True时表示该叶子节点被取反 ("!")
Example
def adapter(leaf, in_or, negated): left, op, right = leaf ok, ko = (1, 2) if not negated else (2, 1) if op == "=" return [(left, "=", ok)] elif op == "!=": return [(left, "=", ko)] return [leaf]
除非
force_adapt为True,否则adapter仅对那些将model的old字段作为叶子节点left部分的 最后一部分 使用的叶子节点调用。在后一种情况下,如果字段出现在路径中的任何位置,则会调用适配器,这仅对关系字段有用。适配器返回的域不需要在输入叶子节点的
left部分将old字段替换为new。无论如何,替换都会应用于适配器返回的整个域。适配器的通常用途是修改操作符和输入叶子节点的right部分。如果未设置adapter,则仅执行替换操作。Example
当将
"field1"替换为"field2"时,会发生以下情况:("foo.bar.baz.field1", "=", 1)仅在foo.bar.baz指向的记录属于请求的model时才会被调整。("foo.field1.baz", "=", 1)即使foo指向model,也不会被调整,除非force_adapt为True,因为field1不是此叶子节点中left的最后一部分。
注解
此函数将替换所有 标准 域字段中的域,包括过滤器、仪表板和已知表示域的标准字段。
SQL¶
用于与 PostgreSQL 交互的实用函数。
- parallel_execute(cr, queries, logger=<Logger odoo.upgrade.util.pg (WARNING)>)[源代码]¶
并行执行查询。
Example
util.parallel_execute(cr, [util.format_query(cr, "REINDEX TABLE {}", t) for t in tables])
小技巧
如果希望加速单个查询,请参见
explode_execute()。- 参数
- 返回
每个查询运行后的
cr.rowcount总和- 返回类型
警告
由于
cr.rowcount的特性,此函数的返回值可能低估了实际受影响记录的数量。例如,当某些记录因ondelete子句而被删除或更新时,它们不会被计入。作为副作用,游标将被提交。
注解
如果发生并发问题,失败的查询将按顺序重试。
- format_query(cr, query, *args, **kwargs)[源代码]¶
安全格式化查询。
传递给此函数的
str参数假定为 SQL 标识符。在使用str.format()展开之前,它们会被包裹在双引号中。任何其他 psycopg2.sql.Composable 也是允许的。这包括ColumnList,另请参见get_columns()。Example
>>> util.format_query(cr, "SELECT {0} FROM {table}", "id", table="res_users") SELECT "id" FROM "res_users"
- 参数
query (str) – 要格式化的查询,可以像
str.format()一样使用大括号{}
- explode_execute(cr, query, table, alias=None, bucket_size=10000, logger=<Logger odoo.upgrade.util.pg (WARNING)>)[源代码]¶
并行执行查询。
查询按 ID 分桶拆分,然后由工作线程并行处理。如果查询未包含特殊的
{parallel_filter}值,则会将其添加到最后一个WHERE子句中(如果未找到WHERE子句,也可能新增)。如果查询已经包含过滤器,则不执行任何操作。过滤器始终扩展为拆分策略。拆分时,每个单独查询更新的 ID 数量不超过bucket_size。Example
util.explode_execute( cr, ''' UPDATE res_users u SET active = False WHERE (u.login LIKE 'dummy' OR u.login = 'bob') AND {parallel_filter} ''', table="res_users" alias="u", )
- 参数
query (str) – 要执行的查询。
table (str) – 查询的主表名称,用于拆分处理
alias (str) – 查询中为主表使用的别名
bucket_size (int) – 用于拆分处理的 ID 桶大小
logger (
logging.Logger) – 用于报告进度的日志记录器
- 返回
每个查询运行后的
cr.rowcount总和- 返回类型
警告
调用者需要确保查询不会在不同的桶中更新相同的记录。建议不要对具有自引用的表使用此函数执行
DELETE查询,因为可能存在ON DELETE影响。更多详细信息,请参见parallel_execute()。
- create_column(cr, table, column, definition, **kwargs)[源代码]¶
创建列。
此函数仅在列不存在时创建该列。如果现有列的类型不同,它会记录错误。如果设置了
fk_table,它将确保外键已设置,并在必要时进行更新,同时应用正确的on_delete_action(如果有)。
- alter_column_type(cr, table, column, type, using=None, where=None, logger=<Logger odoo.upgrade.util.pg (WARNING)>)[源代码]¶
更改列的类型。
通过使用临时列和并行 UPDATE 查询高效完成。
- remove_constraint(cr, table, name, cascade=False, warn=True)[源代码]¶
删除表约束。
此函数从
table中删除名为name的约束。它还会从ir_model_constraint及其 xml_ids 中删除记录,并记录未找到的约束。注解
如果没有名为
name的约束,此函数将尝试删除{table}_{name},后者是 ORM 为从_sql_constraints创建的约束使用的默认名称。
- class ColumnList(list_=(), quoted=())[源代码]¶
封装表示列名称的元素列表。
生成的对象可以渲染为带有前导/尾随逗号或别名的字符串。
Example
>>> columns = ColumnList(["id", "field_Yx"], ["id", '"field_Yx"'])
>>> list(columns) ['id', '"field_Yx"']
>>> columns.using(alias="t").as_string(cr._cnx) '"t"."id", "t"."field_Yx"'
>>> columns.using(leading_comma=True).as_string(cr._cnx) ', "id", "field_Yx"'
>>> util.format_query(cr, "SELECT {} t.name FROM table t", columns.using(alias="t", trailing_comma=True)) 'SELECT "t"."id", "t"."field_Yx", t.name FROM table t'
>>> columns = ColumnList.from_unquoted(cr, ["foo", "BAR"])
>>> list(columns) ['"foo"', '"BAR"']
>>> list(columns.iter_unquoted()) ['foo', 'BAR']
注解
此类最好通过
get_columns()使用
- get_common_columns(cr, table1, table2, ignore=('id',))[源代码]¶
返回两个表中都存在的列列表。
- 参数
- 返回
两个表中都存在的列名称列表
- 返回类型
- rename_table(cr, old_table, new_table, remove_constraints=True)[源代码]¶
重命名表。
此函数将表
old_table重命名为new_table,同时还会重命名其主键(和序列)、索引以及外键。
- create_m2m(cr, m2m, fk1, fk2, col1=None, col2=None)[源代码]¶
确保多对多表存在或被创建。
此函数创建与多对多字段关联的表。如果表已存在,则对其运行
fixup_m2m()。表名可以自动生成,应用与 ORM 相同的逻辑。为此,请为m2m参数使用值 “auto”。
- bulk_update_table(cr, table, columns, mapping, key_col='id')[源代码]¶
基于映射更新表。
每个
mapping条目为key_col值与键匹配的行定义指定columns的新值。Example
# single column update util.bulk_update_table(cr, "res_users", "active", {42: False, 27: True}) # multi-column update util.bulk_update_table( cr, "res_users", ["active", "password"], { "admin": [True, "1234"], "demo": [True, "5678"], }, key_col="login", )
- 参数
警告
映射中的值将被转换为目标列的类型。此函数设计用于更新标量值,避免通过映射设置数组或 JSON 数据。
杂项¶
各种独立函数。
- version_gte(version)[源代码]¶
返回当前运行的 Odoo 版本是否大于或等于
version。此函数对于适用于多个版本的升级脚本中的条件执行非常有用,例如
0.0.0脚本。
- version_between(a, b)[源代码]¶
返回当前运行的 Odoo 版本是否在范围
[a,b]内。另请参见
version_gte()注解
边界值包含在内。
- expand_braces(s)[源代码]¶
展开输入中的大括号。
Example
>>> util.expand_braces("a_{this,that}_b") ['a_this_b', 'a_that_b']
- 参数
s (str) – 要展开的字符串,必须包含且仅包含一对大括号。
- 返回
展开后的输入
- import_script(path, name=None)[源代码]¶
导入升级脚本。
此函数允许从其他升级脚本中导入函数到当前脚本中。
Example
在
mymodule/15.0.1.0/pre-migrate.py中def my_util(cr): # do stuff
在
myothermodule/16.0.1.0/post-migrate.py中from odoo.upgrade import util script = util.import_script("mymodule/15.0.1.0/pre-migrate.py") def migrate(cr, version): script.my_util(cr) # reuse the function
此函数返回一个 Python
module。>>> util.import_script("base/0.0.0/end-moved0.py", name="my-moved0") <module 'my-moved0' from '/home/odoo/src/upgrade-util/src/base/0.0.0/end-moved0.py'>
- skippable_cm()[源代码]¶
返回一个上下文管理器,允许另一个上下文管理器不产生值。
参见
edit_view()获取示例用法。
- chunks(iterable, size, fmt=None)[源代码]¶
将
iterable拆分为大小为size的块,并使用fmt函数包装每个块。此函数对于将大量输入数据拆分为可独立处理的小块非常有用。
Example
>>> list(chunks(range(10), 4, fmt=tuple)) [(0, 1, 2, 3), (4, 5, 6, 7), (8, 9)] >>> ' '.join(chunks('abcdefghijklm', 3)) 'abc def ghi jkl m'
- 参数
iterable (iterable) – 要拆分的可迭代对象
size (int) – 块大小
fmt (function) – 应用于每个块的函数,当传入
None时,fmt在iterable为字符串时变为"".join,否则为iter。
- 返回
一个生成器,迭代
fmt应用于每个块的结果