一、引入
作为飞书多维表的深度使用者,经常需要将一些数据库的数据同步到多维表上,在数据写入之前,一般需要新建数据表和字段。当通过网页端界面新建字段时,如果字段少,还能接受手动一个个创建,不过一旦字段比较多,操作起来特别繁琐,机械地重复着,有点怀疑人生。
这种情况下,急需一个更加自动化的方式来解决!这不,飞书官方便提供了这样的 API 方便我们快速建数据表和字段。
注:数据表和多维表不是同个东西,多维表包含数据表,一个多维表中可以创建多个数据表,和 Excel 的逻辑差不多,多维表类比 Excel 文件,数据表类比 Sheet 表,区别参考如下:
本文就来探讨下如何通过飞书的 API 在多维表中创建数据表并插入数据,主要包含三方面:
- 创建数据表
- 对表的列进行增减等操作
二、创建数据表
2.1 多维表权限问题
注意:如果多维表不是通过应用创建,需要添加权限。
添加权限的方法也很简单,打开多维表,然后在右上角有三个横向的点,点击它,在调出的窗口中选择更多>添加文档应用。
在弹出的界面输入应用名称进行搜索,选中应用,配置编辑或管理权限,点击添加即可。
2.2 建数据表 API 调试
💡API 文档:新建一个数据表
获得权限之后,才能使用应用操作多维表。
创建数据表,至少需要三个配置项:数据表名称、视图名称、一个列。可查看示例值:
{
"table": {
"name": "数据表名称",
"default_view_name": "默认的表格视图",
"fields": [
{
"field_name": "多行文本",
"type": 1
}
]
}
}
打开 API 调试台,需要输入 app_token 和 请求体。
如何获取 app_token?
- 如果是通过 API 创建多维表,可以通过响应体提取到 app_token,响应体的结构如下。这种情况下,可以通过
response.json().get('data').get('app').get('app_token')
获取,或者参考 飞书 API 2-2 直接调用函数cre_bitable()
获取。
- 如果是针对已经创建好的多维表,查看地址栏获取
请求体可以使用默认的示例值,输入 app_token 和请求体之后,开始调试。
请求成功之后,会返回默认的视图 ID 和数据表的 ID,结果参考如下:
查看多维表,新建了一个名叫“数据表名称”的数据表,有一个默认视图叫默认的表示视图,该表有一列,叫多行文本。
三、字段操作
如果对现有的多维表的字段结构不满意,可以对数据表的字段进行新增、删除、更新操作。
对数据表进行字段操作都需要传递“app_token”和“table_id”。不同接口还有一些别的要求,新增字段需要传递请求体、更新字段需要传递“field_id”和请求体、删除字段需要传递“field_id”。
下文都通过上面创建的数据表来操作,所以 app_token 和 table_id 都是一样的,只讨论额外需要的参数。
3.1 新增字段
💡API 文档:新增字段
新增字段的请求体比较简单,一个字段名和字段类型即可,示例值如下:
{
"field_name":"多行文本",
"type":1
}
类型“1”表示1:多行文本,在前面 飞书API(5)中介绍 28 种数据类型的时候,介绍过该类型包含了三种子类型:多行文本、条码和 Email。默认是使用第一个,如果要使用后面的值类型,需要传递“ui_type”参数。
数据类型编码 | 数据类型中文描述 | 数据类型对应英文描述 |
---|---|---|
1 | 多行文本、条码 | Text,Barcode |
1 | Email邮箱 |
比如,我要创建一个 条码,可使用以下请求体
{
"field_name":"条码",
"type":1,
"ui_type":"Barcode"
}
成功之后,可以看到响应体会有相关字段的返回说明
一个 bug:虽然 Email 也是类型“1”,但是指定:“ui_type”:“Email” 之后,并不能成功创建 Email 类型的字段
在多维表上面显示和返回一致,是一个文本类型,而不是 Email。
3.2 更新字段
💡API 文档:更新字段
创建不了 Email,通过更新试试能不能处理。
相比新增字段,更新字段需要多传递一个参数:field_id,从新增的响应体可以了解到,邮箱字段的 field_id 是“fldhROhC0C”。请求体和新增字段时一致,发起调试请求后,报错了:DataNotChange(数据无变更)。说明更新字段也无法更改为“Email”类型。
那么如果要使用“Email”字段,就只能通过界面进行创建或修改,下面展示修改的的方法,创建直接点击添加字段选择“Email”即可。
换个需求:将第一个字段多行文本修改为自增数字的序号,多行文本字段的“field_id”为“fldvonP6L8”,请求体如下:
{
"field_name":"序号",
"type":1005,
"ui_type":"AutoNumber"
}
调试结果如下,修改成功
查看多维表,已更新过来。
如果不知道“field_id”,怎么办?
调用列出字段的 API,填入“app_token”和“table_id”两个参数即可发起调试,查看对应数据表的字段信息。(💡API 文档:列出字段)
3.3 删除字段
💡API 文档:删除字段
把条码字段删除。把“field_id”参数填写之后发起调试,便可以把条码字段删除。
查看多维表,已删除条码字段。
3.4 字段说明
飞书的字段类型有两个级别,主字段类型(“type”)和 ui 类型(“ui_type”)。
前面说到飞书共有 28 种数据类型,这个是指字段的 ui 类型(“ui_type”),主字段类型(“type”)是23种类型,多行文本、条码、邮箱这三类 ui 类型统一为多行文本类型,数字、进度、货币、评分这四类 ui 类型统一为数字类型。
能通过接口创建的字段类型如下:
- “type”类型支持:1,2,3,4,5,7,11,13,15,17,18,20,21,22,23,1001,1002,1003,1004,1005。
- “ui_type”类型支持:Text,Barcode,Number,Progress,Currency,Rating,SingleSelect,MultiSelect,DateTime,Checkbox,User,GroupChat,Phone,Url,Attachment,SingleLink,Formula,DuplexLink,Location,CreatedTime,ModifiedTime,CreatedUser,ModifiedUser,AutoNumber。
换个说法,不支持以下 4 种字段类型通过 API 创建:邮箱、查找引用、流程、按钮。
虽然其他的支持接口创建,但是有一些需要加上字段属性(“property”):进度、货币、评分、单向关联、双向关联。
- 进度:需要在“property”中指定格式
- 货币:需要在“property”中指定格式和货币类型
- 评分:需要在“property”中指定格式和大小值
- 单向关联:需要在“property”中指定引用的目标表和选择类型(单选/双选)
- 双向关联:需要在“property”中指定引用的目标表和选择类型(单选/双选)和目标表的字段名
注意:公式如果没有传递“property”参数指定计算公式,是一个空公式。更多信息科参考:字段编辑指南。
在创建数据表时,可以传递以下请求体,创建一个包含所有支持通过 API 创建的字段的数据表。
特别注意单向关联和双向关联中的“property”的“table_id”的值需要修改为你的多维表下的其他数据表的“table_id”,如果没有,需要去掉该字段,否则将创建失败。
{
"table": {
"name": "所有支持类型字段的数据表",
"default_view_name": "一个视图",
"fields": [
{"field_name": "自动编号","type": 1005,"ui_type": "AutoNumber"},
{"field_name": "多行文本","type": 1,"ui_type": "Text"},
{"field_name": "条码","type": 1,"ui_type": "Barcode"},
{"field_name": "数字","type": 2,"ui_type": "Number"},
{"field_name": "进度","type": 2,"ui_type": "Progress",
"property": {"formatter": "0.00%","min": 0.1,"max": 4,"range_customize": true}
},
{"field_name": "货币","type": 2,"ui_type": "Currency",
"property": {"formatter": "0.0", "currency_code": "CNY" }
},
{"field_name": "评分","type": 2,"ui_type": "Rating",
"property": {"formatter": "0", "min": 0, "max": 5, "rating": {"symbol": "star" }}
},
{"field_name": "单选","type": 3,"ui_type": "SingleSelect"},
{"field_name": "多选","type": 4,"ui_type": "MultiSelect"},
{"field_name": "日期","type": 5,"ui_type": "DateTime"},
{"field_name": "复选框","type": 7,"ui_type": "Checkbox"},
{"field_name": "人员","type": 11,"ui_type": "User"},
{"field_name": "电话号码","type": 13,"ui_type": "Phone"},
{"field_name": "超链接","type": 15,"ui_type": "Url"},
{"field_name": "附件","type": 17,"ui_type": "Attachment"},
{"field_name": "单向关联","type": 18,"ui_type": "SingleLink",
"property": {"multiple": true,"table_id": "tblwZPx4ezszTIEh" }
},
{"field_name": "公式","type": 20,"ui_type": "Formula"},
{"field_name": "双向关联","type": 21,"ui_type": "DuplexLink",
"property": {"multiple": false,"table_id": "tblwZPx4ezszTIEh","back_field_name": "双向关联-自动生成" }
},
{"field_name": "地理位置","type": 22,"ui_type": "Location"},
{"field_name": "群组","type": 23,"ui_type": "GroupChat"},
{"field_name": "创建时间","type": 1001,"ui_type": "CreatedTime"},
{"field_name": "最后更新时间","type": 1002,"ui_type": "ModifiedTime"},
{"field_name": "创建人","type": 1003,"ui_type": "CreatedUser"},
{"field_name": "修改人","type": 1004,"ui_type": "ModifiedUser"}
]
}
}
四、小结
本文探讨了如何通过 API 接口创建飞书多维表的数据表,在字段比较多的时候,方便快速创建表,而不需要在 UI 界面机械操作。基本步骤如下:
- 授权:给应用开通多维表的写权限;
- API 建表:梳理字段并调用 API 接口建表。
同时,探讨了字段的新增、更新和删除,以及介绍了多维表所有字段类型的特性。
- 字段新增:需要“app_token”、“table_id”、字段名及数据类型;
- 字段更新:需要“app_token”、“table_id”、“field_id”、新字段名及数据类型;
- 字段删除:需要“app_token”、“table_id”和“field_id”。
最后,28 种字段类型的梳理如下:
序号 | type | ui_type | 中文描述 | API 支持 | 说明 |
---|---|---|---|---|---|
1 | 1 | Text | 多行文本 | 支持 | |
2 | 1 | Barcode | 条码 | 支持 | |
3 | 1 | 邮箱 | 不支持 | ||
4 | 2 | Number | 数字 | 支持 | |
5 | 2 | Progress | 进度 | 支持 | 需要 property |
6 | 2 | Currency | 货币 | 支持 | 需要 property |
7 | 2 | Rating | 评分 | 支持 | 需要 property |
8 | 3 | SingleSelect | 单选 | 支持 | |
9 | 4 | MultiSelect | 多选 | 支持 | |
10 | 5 | DateTime | 日期 | 支持 | |
11 | 7 | Checkbox | 复选框 | 支持 | |
12 | 11 | User | 人员 | 支持 | |
13 | 13 | Phone | 电话号码 | 支持 | |
14 | 15 | Url | 超链接 | 支持 | |
15 | 17 | Attachment | 附件 | 支持 | |
16 | 18 | SingleLink | 单向关联 | 支持 | 需要 property |
17 | 19 | Lookup | 查找引用 | 不支持 | |
18 | 20 | Formula | 公式 | 支持 | 空公式 |
19 | 21 | DuplexLink | 双向关联 | 支持 | 需要 property |
20 | 22 | Location | 地理位置 | 支持 | |
21 | 23 | GroupChat | 群组 | 支持 | |
22 | 24 | Stage | 流程 | 不支持 | |
23 | 1001 | CreatedTime | 创建时间 | 支持 | |
24 | 1002 | ModifiedTime | 最后更新时间 | 支持 | |
25 | 1003 | CreatedUser | 创建人 | 支持 | |
26 | 1004 | ModifiedUser | 修改人 | 支持 | |
27 | 1005 | AutoNumber | 自动编号 | 支持 | |
28 | 3001 | Button | 按钮 | 不支持 |
附录:代码小结
import requests
import json
def cre_data_sheet(access_token,app_token,request_body):
url = f"https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables"
payload = json.dumps(request_body)
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {access_token}'
}
payload = json.dumps(request_body)
response = requests.request("POST", url, headers=headers, data=payload)
code = response.json()['code']
if code == 0:
table_id = response.json().get("data").get("table_id")
tb_name = request_body["table"]["name"]
print(f"成功新建数据表:{tb_name},数据表的 table_id 为:{table_id}。关联函数:cre_data_sheet。")
return table_id
else:
msg = response.json().get("msg")
raise f"新建数据表失败,失败信息:{msg}。关联函数:cre_data_sheet。"
def add_field(access_token,app_token,table_id,request_body):
url = f"https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/fields"
payload = json.dumps(request_body)
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {access_token}'
}
response = requests.request("POST", url, headers=headers, data=payload)
code = response.json()['code']
if code == 0:
field_id = response.json().get("data").get("field").get("field_id")
field_name = request_body["field_name"]
print(f"成功新增字段:{field_name},该字段的 field_id 为:{field_id}。关联函数:add_field。")
return table_id
else:
msg = response.json().get("msg")
raise f"新增字段失败,失败信息:{msg}。关联函数:add_field。"
def update_field(access_token,app_token,table_id,field_id,request_body):
url = f"https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/fields/{field_id}"
payload = json.dumps(request_body)
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {access_token}'
}
response = requests.request("PUT", url, headers=headers, data=payload)
code = response.json()['code']
if code == 0:
field_id = response.json().get("data").get("field").get("field_id")
field_name = request_body["field_name"]
print(f"成功更新字段:{field_name}。关联函数:update_field。")
return table_id
else:
msg = response.json().get("msg")
raise f"更新字段失败,失败信息:{msg}。关联函数:update_field。"
def del_field(access_token,app_token,table_id,field_id):
url = f"https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/fields/{field_id}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {access_token}'
}
response = requests.request("DELETE", url, headers=headers)
code = response.json()['code']
if code == 0:
field_id = response.json().get("data").get("field_id")
field_name = request_body["field_name"]
print(f"成功删除字段:{field_name}。关联函数:del_field。")
return table_id
else:
msg = response.json().get("msg")
raise f"删除字段失败,失败信息:{msg}。关联函数:del_field。"
def get_tenant_access_token(app_id, app_secret):
url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"
payload = json.dumps({
"app_id": app_id,
"app_secret": app_secret
})
headers = {'Content-Type': 'application/json'}
response = requests.request("POST", url, headers=headers, data=payload)
tenant_access_token = response.json()['tenant_access_token']
print(f'成功获取tenant_access_token:{tenant_access_token}。关联函数:get_table_params。')
return tenant_access_token
def main(request_body):
app_id = 'your_app_id'
app_secret = 'your_app_secret'
app_token = 'your_app_token'
request_body = {
"table": {
"name": "数据表名称",
"default_view_name": "默认的表格视图",
"fields": [
{
"field_name": "多行文本",
"type": 1
}
]
}
}
access_token = get_tenant_access_token(app_id, app_secret)
table_id = cre_data_sheet(access_token,app_token,request_body)
# # 新增字段
# request_body = {
# "field_name": "条码",
# "type": 1,
# "ui_type": "Barcode"
# }
# add_field(access_token,app_token,table_id,request_body)
# # 更新字段
# request_body = {
# "field_name":"序号",
# "type":1005,
# "ui_type":"AutoNumber"
# }
# field_id = "your_field_id"
# update_field(access_token,app_token,table_id,field_id,request_body)
# # 删除字段
# field_id = "your_field_id"
# del_field(access_token,app_token,table_id,field_id)
if __name__ == '__main__':
main()