InvenTree 物理单位体系详解:基于 Pint 的单位转换、工程记数法与自定义单位

发布时间:2026/9/16 11:32:12
InvenTree 物理单位体系详解:基于 Pint 的单位转换、工程记数法与自定义单位 InvenTree 物理单位体系详解基于 Pint 的单位转换、工程记数法与自定义单位【免费下载链接】InvenTreeOpen Source Inventory Management System项目地址: https://gitcode.com/GitHub_Trending/in/InvenTreeInvenTree 使用 Python 的 Pint 库为库存管理引入了真实的物理单位能力零件计量单位、供应商包装单位与零件单位之间的自动换算、参数模板的单位兼容性校验以及通过数据库热加载的自定义单位定义。本文基于官方文档 Physical Units 展开并结合 单位转换核心实现、CustomUnit 数据模型 与 单位 API 的源码完整讲解单位体系的输入格式、支持范围与自定义方法读完后可掌握在 InvenTree 中正确配置单位、排查单位校验错误并扩展自定义单位的全部实战要点。核心机制Pint 单元注册表与热重载物理单位的底层支撑是 Pint 的UnitRegistry。InvenTree 的 new_base_registry() 函数构造了一个带有项目定制的基础注册表以pint.UnitRegistry(autoconvert_offset_to_baseunitTrue)创建注册表偏移量单位如摄氏温度可正确转换到基准单位定义温度别名alias degC Celsius、degF、degK方便使用degC这类直观写法将R覆写为ohm电阻单位因为 Pint 默认将 R 视为 SI 前缀定义一批标准附加单位见下文 内置自定义单位 一节。在此基础之上reload_unit_registry() 会从数据库读取全部CustomUnit记录逐条调用registry.define()注入自定义单位并对所有自定义单位定义字符串计算 MD5 哈希作为注册表状态指纹。整个流程有三个关键设计注册表缓存get_unit_registry() 全局缓存注册表实例仅当数据库中的单位哈希发生变化时才重建避免每次请求都扫描数据库哈希同步指纹同时写入全局设置_UNIT_REGISTRY_HASH与会话缓存跨进程也能感知变化在数据导入、备份、数据库迁移或测试模式下见 can_cache_registry()会跳过缓存防止在这些阶段触发不必要的数据库访问保存即重载after_custom_unit_updated 信号接收器在自定义单位被保存或删除后强制调用reload_unit_registry()使新单位定义立即全局生效无需重启服务。此外InvenTree 全局的日志级别将 Pint 内部日志抑制到 ERROR避免 Pint 在解析失败时产生大量噪声日志。单位转换跨兼容单位的自动换算InvenTree 使用 Pint 在不同量纲单位之间进行换算质量克、千克、磅等或长度毫米、英寸等这类量纲兼容的单位可以互相转换而不同量纲之间如长度与质量则会被拒绝。convert_value 与 convert_physical_value 的分工InvenTree/conversion.py 中提供两个层级的转换入口convert_value(value, unit)将输入转为pint.Quantity若输入本身是无维度dimensionless量则直接取基准幅值再挂上目标单位否则调用value.to(unit)做量纲换算单位不兼容时抛出异常convert_physical_value(value, unit, strip_unitsTrue)面向业务场景的宽容解析器是各数据模型实际调用的函数详见下节。宽容解析一次输入、多次尝试convert_physical_value的解析策略值得注意L287-L331空值直接抛出ValidationErrorNo value provided预处理英制长度简写见下文构造一个尝试序列原始值 → 工程记数法归一化后的值 → 若提供了目标单位再追加值单位两种组合依次尝试解析取第一个成功的结果全部失败时按是否提供了单位分别报 Could not convert X to Y 或 Invalid quantity provided成功解析后统一转为float幅值返回strip_unitsTrue或将pint.Quantity原样返回strip_unitsFalse供调用方做后续数值计算。供应商包装数量的换算约束SupplierPart.clean() 展示了单位转换在采购场景的落地供应商零件的pack_quantity每包数量会被转换为零件自身单位下的原生值空pack_quantity等价于1通过convert_physical_value(pack_quantity, self.part.units, strip_unitsFalse)尝试换算若零件未指定单位则包装数量必须为无维度量否则报 Pack units must be compatible with the base part units换算结果必须大于零计算出的幅值写入pack_quantity_native字段用于后续库存换算。这正是文档所述单位转换规则确保只能提供兼容单位类型的源码实现。特殊输入格式工程记数法、科学记数法与英制简写InvenTree 对数值输入做了多项工程化的格式支持以下规则均可在 convert_physical_value 及其辅助函数中找到对应实现。工程记数法Engineering Notation工程记数法允许把 SI 前缀直接拼在数值尾部前缀替代小数点的位置。文档给出的有效示例输入解析结果10k310,300即 10.3k10M310,000,000即 10.0M3n020.00000000302即 3.02n实现位于 from_engineering_notation()用正则(\d)([a-zA-Z])(\d)(.*)匹配数字 字母前缀 数字 可选后缀的结构将1K2 ohm归一化为1.2K ohm、将10n005F归一化为10.005nF再交给 Pint 解析。函数 docstring 中还给出了电阻场景示例如8R6 - 8.6R配合前文R ohm的注册表覆写输入8R6即表示 8.6 欧姆。科学记数法Scientific Notation科学记数法同样受支持文档示例1E-30.0011E31000-123.45E-3-0.12345注意大小写敏感1E3有效而1e3无效。这是 Pint 对指数形式的解析约定输入时务必使用大写字母E。英尺与英寸简写英制长度支持单引号/双引号简写文档示例33 英尺66 英寸实现上L293-L298当字符串恰好包含一个且以结尾时将其替换为feet包含一个且以结尾时替换为inches。因此复合测量如36不受支持——该值虽包含一个但结尾是不满足替换条件Pint 无法解析解析将失败。大小写敏感规则Pint 库本身大小写敏感kg有效而KG无效。使用 SI 前缀时尤其要留意区分例如kkilo千与KKelvin开尔文、Mmega兆与mmeter米。单位支持范围Part、Supplier Part 与 Parameter文档列出了三个使用物理单位的 InvenTree 子系统均可在源码中确认其字段与校验器Part零件Part 模型的 units 字段 为最长 20 字符的可选字符串挂接了 validate_physical_units 校验器——该函数尝试将输入交给注册表解析捕获UndefinedUnitError、TokenError等异常后统一抛出 Invalid physical unit空值则直接放行。Supplier Part供应商零件供应商零件 使用真实单位在供应商包装数量与内部库存数量之间换算约束逻辑见上文 SupplierPart.clean()只有与基础零件单位量纲兼容或零件无单位时的无维度量的包装单位才能通过校验。Parameter参数参数模板ParameterTemplate可指定计量单位具体参数Parameter的数值在保存时会被 convert_physical_value 转换并写入data_numeric字段从而支持按数值过滤参数模板的单位字段同样挂接validate_physical_units校验器。从 common/filters.py 的过滤逻辑看参数列表 API 还能对带单位的数值做解析比较实现跨单位的数值筛选。这些解析与校验行为的正确性由 InvenTree/tests.py 中的单元测试覆盖测试用例涵盖了米制转换、科学/工程记数、功率单位W、复合格式如henry / km等场景可作为验证单位行为时的参照。自定义单位Custom UnitsPint 开箱即用地提供了大量单位但特定应用场景可能需要更多。InvenTree 允许自定义单位来引入新的物理量、关联现有单位或仅为已有单位提供别名。内置自定义单位InvenTree 内置的自定义单位 在new_base_registry()中定义单位说明源码定义piece单个物品无维度piece 1each单个物品无维度别名eaeach 1 eadozen十二个物品无维度别名dzdozen 12 dzhundred一百个物品无维度hundred 100thousand一千个物品无维度thousand 1000此外还包含温度别名degC/degF/degK与电阻单位覆写R ohmL101-L107。CustomUnit 数据模型自定义单位由 CustomUnit 模型持久化包含三个字段name单位名称最长 50 字符必填且唯一symbol可选单位符号最长 10 字符如 meter 的m同样唯一definition单位定义最长 50 字符必填语法遵循 Pint 的单位定义规则如dog_year 52 * day dy这种形式。fmt_string() 将三字段拼装成 Pint 定义字符串{name} {definition}若存在 symbol 则追加 {symbol}。保存时的多层校验CustomUnit.clean() 在保存前执行严格的校验链name必须是合法的 Python 标识符isidentifier()definition单独作为registry.Quantity(definition)验证必须是合法物理量表达式将完整fmt_string()注入当前注册表定义一遍捕获命名冲突等错误调用 build_candidate_unit_registry() 构建一个临时注册表——载入所有现存自定义单位更新时排除自身加上新定义然后尝试解析该单位的兼容单位集合专门用于捕获循环引用这类只有所有定义同时加载后才暴露的问题失败时报 Unit definition results in a circular or invalid reference。任何一层失败都会以ValidationError返回给前端单位不会被写入数据库。创建与管理入口在 设置面板 的Physical Units标签页中可以查看、编辑和创建自定义单位。对应的后端 API 是 CustomUnitViewset标准的 CRUD 视图集挂载在路由units上写操作要求 staff 权限IsStaffOrReadOnlyScope额外的all子端点遍历整个注册表返回default_system默认单位制、available_systems可用单位制列表与available_units每个单位的别名状态、兼容单位列表、是否无维度供前端下拉选择该端点对单个坏单位做了容错get_unit 捕获异常并记日志避免一个循环定义拖垮整个列表。无维度自定义单位可以为无维度量定义自定义单位为本来无单位的数值提供业务语境如台、批等。要创建无维度单位必须将定义字面量写为1例如unit_name 1这样新单位就与piece、each等一样属于无维度单位可相互转换并参与计数类换算。创建界面如上文配图所示。实践要点小结输入格式优先使用纯数字 单位的组合需要紧凑写法时可用工程记数法10k3或科学记数法1E-3注意大写E英制长度可用3、6简写但不要写36复合形式大小写单位与 SI 前缀均区分大小写kg≠KGk≠Km≠M兼容性Part 单位、Supplier Part 包装数量、Parameter 模板单位三者的兼容关系由同一套 Pint 注册表保证校验失败信息Invalid physical unit、Could not convert X to Y可直接定位到输入问题自定义单位定义语法遵循 Pint 规则无维度单位定义必须为字面量1命名冲突或循环引用会在保存时被提前拦截且保存后注册表自动热重载无需重启。【免费下载链接】InvenTreeOpen Source Inventory Management System项目地址: https://gitcode.com/GitHub_Trending/in/InvenTree创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考