Baserow 公式字段完全指南:从concat入门到 SQL 编译原理
【免费下载链接】baserowBuild databases, automations, apps & agents with AI — no code. Open source platform available on cloud and self-hosted. GDPR, HIPAA, SOC 2 compliant. Best Airtable alternative.项目地址: https://gitcode.com/GitHub_Trending/ba/baserow
Baserow 的公式字段(Formula Field)允许你为字段中的每一行单元格基于一段公式动态计算结果,语法与主流电子表格工具类似,且拥有不断扩充的函数库。本指南以 Understanding Baserow Formulas 为核心,逐步讲解如何编写、调试 Baserow 公式,并深入源码剖析公式从文本到 PostgreSQL 查询的完整编译链路。读完本文,你将掌握公式字段的日常用法(文本拼接、数值运算、条件判断、日期计算)、常见错误排查,以及函数注册、类型推导、运算符重载等底层机制。
什么是 Baserow 公式字段
一个 Baserow 公式字段让你创建"内容由你提供的 Baserow 公式计算而来"的字段。Baserow 公式本质上是一段按照特定规则书写的文本,Baserow 能够解析并执行它。例如文本1+1就是一个合法的 Baserow 公式,它会在每一行计算出结果2。
与普通字段不同,公式字段只有一个公式,整个字段的所有单元格都由该公式按行计算得出——这决定了公式字段的单元格无法被手动编辑(详见文末 FAQ)。
一个简单的公式示例
假设你有一张表,其中包含一个名为text field的普通文本字段,共有 3 行,分别存放文本one、two和three。此时如果你新建一个公式字段,并写入公式concat('Number', field('text field')),得到的表格将是:
| text field | formula field |
|---|---|
| one | Number one |
| two | Number two |
| three | Number three |
可以看到,公式字段的每一个单元格都是基于同一行text field的单元格值计算出来的。公式字段可以引用同表其他字段(甚至其他公式字段),但不能引用自身,循环引用同样被禁止(见 formula-technical-guide.md 的 Field References 章节)。
逐段拆解一个简单公式
我们把concat('Number', field('text field'))拆开来看,理解每一部分的含义:
concat- 这是 Baserow 众多公式函数之一,作用是把所有输入拼接成一段文本。
(- 左括号,表示接下来要开始向函数传入输入参数。
'Number'- 传入
concat的第一个输入,是一个字面量文本Number。在公式中书写文本字面量时,必须用引号包裹(Baserow 同时支持单引号与双引号,见 BaserowFormula.g4 中的SINGLEQ_STRING_LITERAL/DOUBLEQ_STRING_LITERAL词法规则)。
- 传入
,- 逗号,用于分隔函数的多个输入参数。
field('text field')- 传给
concat的第二个输入。field()是对同表字段的引用,参数为该字段名。公式字段每个单元格计算时,这个引用都会被替换为对应行在text field字段中的实际值。理论上你可以继续用逗号追加任意多个输入。
- 传给
)- 右括号,表示函数输入结束,与左括号一一匹配。
什么是公式函数
公式中的函数按各自类型接收一定数量的输入,对输入做计算后产出一个输出。有些函数只接受特定类型的输入,例如datetime_format只接受两个参数:第一个必须是日期(可以是日期字段的引用,也可以是能算出日期的子公式),第二个必须是文本。
从语法层面看,函数调用在 BaserowFormula.g4 中被定义为func_name OPEN_PAREN (expr (COMMA expr)*)? CLOSE_PAREN,即"函数名 + 括号 + 逗号分隔的表达式列表"。所有可用函数都会在编辑公式字段时展开的编辑框中列出,供你随时查阅。
在公式中使用数字
公式天然支持数值计算,标准数学运算符+、-、*、/都可用。你可以在公式中直接书写整数或小数,例如:
(field('number field') + 10.005) / 10优先级遵循 BaserowFormula.g4 中规则的书写顺序:*、/的规则位于+、-之前,因此1+1/2会先计算1/2,这与常规数学优先级一致;括号OPEN_PAREN expr CLOSE_PAREN则用于显式控制结合顺序。
Invalid Number 错误
如果在公式单元格中看到Invalid Number,说明该行的公式执行了以下非法操作之一:
- 将数字除以 0;
- 使用
tonumber函数把文本转换为数字时失败(因为该文本不是合法数字); - 计算出的数字超过了允许的最大值
10^50。
值得一提的细节是,公式结果最终被存入 PostgreSQL 列,数字类型默认与NUMBER_MAX_DECIMAL_PLACES等精度约束相关(参见 fields/models.py),因此超大数值会触发上限校验。
条件计算
需要按条件计算结果时,可使用if函数配合比较运算符。例如下面的公式判断某个日期字段是否为一个月的第一天:
IF(day(field('some date')) = 1, true, false)可用的比较运算符包括>、>=、<=、<、=和!=。此外,从语法定义还可以看到&&(AND)与||(OR)逻辑运算符,它们与比较运算符一样在底层被映射为对应的函数调用(详见下文"运算符即函数")。
使用日期
使用todate函数可以在公式内创建一个常量日期,例如:
todate('2020-01-01 10:20:30', 'YYYY-MM-DD HH:MI:SS')第一个参数是文本形式的日期,第二个参数是该日期文本的格式。日期处理相关的辅助工具可在 core/formula/utils/date.py 中看到实现。
使用日期间隔
两个日期相减会返回它们之间的时间差:
field('date a') - field('date b')date_interval函数则用于在公式内部创建日期/时间间隔。需要基于一个间隔计算新的日期时,可以这样写:
field('my date column') - date_interval('1 year')这条公式的含义是"用my date column的日期减去 1 年",得到一个新的日期。日期与间隔的加减、求差等运算由公式类型系统在类型检查阶段完成类型匹配,再翻译为底层表达式。
FAQ
为什么我不能修改公式字段单元格的值?
公式字段的单元格值无法直接修改,因为整个字段共享同一条公式,所有单元格的值都由它统一计算。如果你已完成计算、想对个别结果做针对性编辑,建议把公式字段转换回普通字段再修改。
删除公式引用的字段会发生什么?
如果一个字段被公式引用,而你删除了被引用的字段,公式字段会变成无效状态并显示错误。修复方式有四种:恢复被删除的字段、新建一个同名的新字段、修改公式使其不再引用被删除的字段、或重命名其他字段使其与引用名匹配。
这一行为背后有完整的机制支撑:删除字段时,系统会把引用它的公式标记为 broken 并报错;当用户创建一个同名新字段(或恢复、重命名字段)时,那些 broken 公式会重新变为有效。因此创建、恢复或重命名字段都可能改变同表其他公式字段的类型,系统会相应地重新检查并重新推导整张表的公式类型(详见 formula-technical-guide.md 的 Field Renaming 章节)。
进阶:公式的底层实现原理
作为开源项目,Baserow 公式的实现是一条完整的技术链路,从语法解析一路走到 SQL 求值。理解它能帮助你更好地掌握公式行为,也为插件扩展提供指引。相关模块位于 backend/src/baserow/contrib/database/formula/ 下。
语法定义与解析器生成
Baserow 公式语言是开源的自研表达式语言,拥有完整的语法/文法定义:
- 语法定义文件见 formula/BaserowFormula.g4 与 formula/BaserowFormulaLexer.g4,采用 ANTLR4 文法;
- 后端用于校验公式合法性并构造 Python AST 的解析器,位于 backend/src/baserow/core/formula/parser/(其中
generated/目录存放 ANTLR 生成的代码,如BaserowFormula.py、BaserowFormulaLexer.py); - 前端浏览器中用于校验公式合法性的解析器同样由该文法生成。
语法文件中还保留了向后兼容的FIELDBYID(按 ID 引用字段)语法,但已标记为 deprecated 不再推荐使用;目前推荐统一使用field('字段名')按名称引用。
Python AST 与类型系统
- 后端内部使用一套 Python 抽象语法树(AST)表示公式,定义在 ast/tree.py,包括字符串/整数/小数/布尔字面量、字段引用、函数调用等节点;
- 类型推导算法位于 types/,其中 types/formula_type.py 定义
BaserowFormulaType、BaserowFormulaValidType与BaserowFormulaInvalidType,types/formula_types.py 则实现了BaserowFormulaTextType、BaserowFormulaNumberType、BaserowFormulaDateType、BaserowFormulaBooleanType等具体类型。
类型系统决定了每个公式字段的"公式类型",并由此影响排序、过滤等行为。
公式最终如何计算:编译为 Django 表达式与 SQL
公式最终会被编译成一条预编译的 SQL 语句,用于在 PostgreSQL 列中计算并存储公式结果。实现路径是:先把 Baserow 公式转换为 Django Expression(核心文件 generator.py 与 django_expressions.py)。
这也解释了为什么公式计算"快":所有行的求值都在数据库侧以 SQL 批处理完成,而非在应用层逐行循环。
函数注册表与插件扩展
每个公式函数都通过实现BaserowFunctionDefinition并注册到formula_function_registry中来定义。注册表本身在 registries.py 中定义:
class BaserowFormulaFunctionRegistry(Registry): name = "formula_function" does_not_exist_exception_class = FormulaFunctionTypeDoesNotExist formula_function_registry = BaserowFormulaFunctionRegistry()插件可以非常容易地新增自定义公式函数:实现一个BaserowFunctionDefinition并注册进formula_function_registry即可。函数具有特定或不限数量的参数,参数可被类型检查并强制为特定类型(否则报错),函数本身拥有返回值类型。丰富的内置函数实现(如concat、if、day、todate、date_interval、tonumber等)集中在 ast/function_defs.py,每个函数都定义了如何将自身转换为 Django Expression 来计算结果。
运算符即函数,支持重载
运算符本质上被实现为"运算符到BaserowFunctionDefinition的映射",例如+运算符只是调用BaserowAdd函数的一种花哨写法;运算符的优先级则由 BaserowFormula.g4 中规则排列顺序决定(越靠前的规则优先级越高)。
同时运算符支持重载:根据输入类型采用不同实现。例如'a' + 'b'会拼接两个字符串,而1 + 2执行数值加法。从 formula_types.py 可以看到,文本类型定义了addable_types(可相加类型)与add方法,其中文本的+被实现为转发到concat函数调用。
类型强制转换与类型格式化选项
类型系统通常会把类型强制转换为普通用户直觉上合理的方式。例如CONCAT(field('a date field'), field('a boolean field'))无需显式把每个参数 cast 成文本即可正常工作。但系统不会允许奇怪的操作,比如拿布尔值去和日期比较,此时会直接给出类型错误,而不是默默返回false。
公式类型还允许用户覆盖格式化选项。举例来说:
- 公式字段
1+1初始被推导为BaserowFormulaNumberType(num_decimal_places=0); - 该推导类型被持久化到
FormulaField模型上(formula_type='number'、num_decimal_places=0,其他类型选项字段为 null),此时所有单元格显示为1; - 用户可以自行编辑这些持久化的类型选项来改变类型与显示方式,例如把
num_decimal_places改为2,对应模型字段同步更新,类型变为BaserowFormulaNumberType(num_decimal_places=2); - 此后单元格显示为
1.00。
这些用户提供的格式化选项在公式整体类型发生变化时会重置,否则会一直保留。
无效公式类型与字段重命名
对于(1+'a')这类类型不合法的公式,系统存在一个 invalid 类型,它会把错误信息存储在FormulaField模型上。由于公式字段可能引用其他字段,每当字段被编辑、删除、恢复或创建时,都可能影响依赖它的其他公式字段。此时系统会构造整张表所有字段的引用树,重新计算每个字段的类型,若类型发生变化则刷新对应公式字段的值——相关迁移与重算逻辑见 formula/migrations/。
重命名字段时,公式中对它的引用会被一并更新为新的字段名,并把更新后的字段返回给浏览器;删除字段时,引用它的公式被标记为 broken 并报错,之后用户新建同名新字段即可让这些公式自动恢复有效。
排序与过滤公式字段
公式字段可以基于自身的BaserowFormulaType参与 Baserow 现有的视图过滤与排序。定义视图过滤器时,通过FormulaFieldType.compatible_with_formula_types辅助函数声明哪些 Formula Type 与该过滤器兼容,即可让公式字段支持相应过滤条件。
测试与验证
后端为公式系统配备了较为完整的测试,可在 backend/tests/baserow/contrib/database/formula/ 与 backend/tests/baserow/core/formula/ 下查看,例如test_baserow_formula_results.py(公式计算结果)、test_rename_field_references.py(字段重命名对公式引用的影响)等,这些测试是理解公式行为边界的最佳参考。
当前不支持的字段类型与未来功能
截至当前版本,以下字段类型还无法在公式中被引用和使用:
- Collaborators(协作者)
- Created by(创建者)
- Last modified by(最后修改者)
- Password(密码)
- AI Prompt
同时,更多公式函数正在持续开发中。如果你有特别需要的函数,可以在官方社区论坛提出,帮助团队确定新函数的优先级。
小结
从用户视角看,Baserow 公式字段是一条"写一段文本 → 全表按行求值"的高效计算方案:用field('字段名')引用同表字段,用concat、if、todate、date_interval等函数与+ - * /、比较、逻辑运算符组合出任意复杂度的计算,并可通过类型格式化选项控制显示效果。从实现视角看,公式语言经历了 ANTLR 文法解析 → Python AST → 类型推导 → Django Expression → PostgreSQL SQL 的完整编译管线,函数注册表机制让插件可以低成本扩展新函数。建议进一步阅读 Baserow Formula 技术指南 获取面向开发者的完整实现细节。
【免费下载链接】baserowBuild databases, automations, apps & agents with AI — no code. Open source platform available on cloud and self-hosted. GDPR, HIPAA, SOC 2 compliant. Best Airtable alternative.项目地址: https://gitcode.com/GitHub_Trending/ba/baserow
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考