飞书配置表数据同步到数据库中
这是我的从飞书取数据的代码
def get_employee_from_feishu():staff_setting = settings.FEISHU_SETTING["sales_order"]["employee"]app_token = staff_setting ["app_token"]table_id = staff_setting ["table_id"]page_token = NonehasMore = Trueemployees = {}while hasMore:staff_data = bitable_service.fetch(app_token, table_id, page_token=page_token)# 格式参考warehouse.jsonhasMore = staff_data["data"]["has_more"]rows = staff_data["data"]["items"]for row in rows:fields = row["fields"]if "姓名" in fields:employee = {"employee_id": fields.get("工号"),"name": fields.get("姓名"),"position": fields.get("人员.职务"),"department": fields.get("部门名"),}employees[employee["name"]] = employeeif not hasMore:breakpage_token = staff_data["data"].get("page_token")return employees
这是我的数据库模型
from django.db import modelsclass EmployeeModel(models.Model):employee_id = models.CharField(max_length=100, unique=True, null=True, blank=True, verbose_name="工号")name = models.CharField(max_length=64, null=True, blank=True, verbose_name="姓名")position = models.CharField(max_length=100, null=True, blank=True, verbose_name="人员.职务")department = models.CharField(max_length=100, null=True, blank=True, verbose_name="部门名")class Meta:db_table = "xss_employee"verbose_name = "员工信息"verbose_name_plural = "员工信息"def __str__(self):return self.name
这是我将飞书数据进入数据库的代码
from core.models import EmployeeModel
from eccang.services.base_data_fetch_service import (get_product_category_from_feishu,get_warehouse_from_feishu,get_employee_from_feishu,get_country_from_feishu,get_monthly_target_from_feishu,get_position_target_from_feishu,
)def sync_employee_to_db():data_dict = get_employee_from_feishu()for data in data_dict.values():if not data["employee_id"]:continue # 跳过无工号数据EmployeeModel.objects.update_or_create(employee_id=data["employee_id"],defaults={"name": data["name"],"position": data["position"],"department": data["department"],})
✅不错的地方
使用分页抓取飞书数据,防止数据量大时漏数据。使用 update_or_create,避免重复插入。过滤掉没有 employee_id 的记录,防止插入空主键。
🔍建议优化点
- 字段容错性检查
在访问字段时使用了 fields.get(),但在 position、department、employee_id 字段不是所有记录都存在时,可能得到 None。可以在构建 employee 字典时加默认值,避免报错或数据不一致:
employee = {"employee_id": fields.get("工号", "").strip(),"name": fields.get("姓名", "").strip(),"position": fields.get("人员.职务", "").strip(),"department": fields.get("部门名", "").strip(),
}
注意 .strip() 可避免导入数据库中的空格异常。
- 避免空姓名造成 key 重复覆盖
使用了 employees[employee[“name”]] = employee,这会以“姓名”作为 key,而如果出现多个同名员工,可能被覆盖,造成数据丢失。
建议改为用 employee_id 做 key,更稳妥:
employees[employee["employee_id"]] = employee
-
字段命名检查
使用的是字段名如 “人员.职务”,这是合法的,但建议在飞书中避免这种带点的字段名,因为它可能与 Python 字段语义混淆,或者将来你要做统一映射时会麻烦。 -
同步逻辑建议加日志(可选)
可以加一点日志输出,方便调试或上线监控:
import logging
logger = logging.getLogger(__name__)def sync_employee_to_db():data_dict = get_employee_from_feishu()created, updated, skipped = 0, 0, 0for data in data_dict.values():if not data["employee_id"]:skipped += 1continueobj, is_created = EmployeeModel.objects.update_or_create(employee_id=data["employee_id"],defaults={"name": data["name"],"position": data["position"],"department": data["department"],})if is_created:created += 1else:updated += 1logger.info(f"员工同步完成:新增 {created} 条,更新 {updated} 条,跳过 {skipped} 条")
✅ 总结
飞书分页抓取
✅
空值处理
⚠️ 建议加默认值 .get(…, “”).strip()
主键唯一性
⚠️ 建议用 employee_id 做 key
数据同步方式
✅ update_or_create 正确
日志与监控
⚠️ 建议加简单日志
字段命名规范性
⚠️ “人员.职务” 命名不够语义清晰,建议优化
下面是这段同步员工数据代码的详细结构分析,包括每一部分的职责、依赖关系、潜在问题和优化建议。
一、函数入口:sync_employee_to_db()
def sync_employee_to_db():
作用:
主函数,负责执行“从飞书多维表格获取员工数据 → 写入本地数据库”的全过程。
二、从飞书获取数据
data_dict = get_employee_from_feishu()
对应的函数:
def get_employee_from_feishu():staff_setting = settings.FEISHU_SETTING["sales_order"]["employee"]app_token = staff_setting ["app_token"]table_id = staff_setting ["table_id"]page_token = NonehasMore = Trueemployees = {}while hasMore:staff_data = bitable_service.fetch(app_token, table_id, page_token=page_token)hasMore = staff_data["data"]["has_more"]rows = staff_data["data"]["items"]for row in rows:fields = row["fields"]if "姓名" in fields:employee = {"employee_id": fields.get("工号"),"name": fields.get("姓名"),"position": fields.get("人员.职务"),"department": fields.get("部门名"),}employees[employee["name"]] = employeeif not hasMore:breakpage_token = staff_data["data"].get("page_token")return employees
作用:
调用 bitable_service.fetch() 与飞书 API 通信;
按页遍历所有员工数据;
将每条员工记录解析为字典格式;
最后以 “姓名” 为 key 构造一个员工字典并返回。
依赖项:
bitable_service.fetch():对飞书多维表格的 API 封装;
settings.FEISHU_SETTING:飞书的 App Token 和表格 ID 来自配置文件;
字段名称如 “工号”, “姓名”, “人员.职务” 是 Feishu 表格中的字段。
潜在问题:
employees[employee[“name”]] = employee:若员工同名会被覆盖;
若某行 fields 缺少必要字段,可能插入 None;
没有 .strip() 清洗字符串,可能出现数据不一致(如 "张三 " vs “张三”);
若飞书接口失败,未处理异常(建议加 try-except);
三、同步到数据库
for data in data_dict.values():if not data["employee_id"]:continue # 跳过无工号数据EmployeeModel.objects.update_or_create(employee_id=data["employee_id"],defaults={"name": data["name"],"position": data["position"],"department": data["department"],})
作用:
遍历每条员工数据;
以 “employee_id” 作为唯一键更新或插入 EmployeeModel;
若数据库中已存在对应 employee_id,则更新其他字段;
若不存在,则创建新记录。
优点:
update_or_create 可以自动处理“增”和“改”,逻辑清晰;
使用 “employee_id” 作为唯一键,符合业务设计;
建议优化:
增加日志记录,比如:新增了多少条、更新了多少条;
对于姓名为空或异常的数据可以加校验;
加异常处理,防止单条数据出错影响整个同步流程。
四、模型结构分析:EmployeeModel
class EmployeeModel(models.Model):employee_id = models.CharField(max_length=100, unique=True, null=True, blank=True, verbose_name="工号")name = models.CharField(max_length=64, null=True, blank=True, verbose_name="姓名")position = models.CharField(max_length=100, null=True, blank=True, verbose_name="人员.职务")department = models.CharField(max_length=100, null=True, blank=True, verbose_name="部门名")class Meta:db_table = "xss_employee"verbose_name = "员工信息"verbose_name_plural = "员工信息"def __str__(self):return self.name
字段解释:
字段名 类型 含义
employee_id CharField 员工工号,设为唯一键
name CharField 员工姓名
position CharField 职位/职务
department CharField 部门名称
建议:
如果业务上“工号”不可能为空,可以设置 null=False, blank=False;
添加 created_at、updated_at 字段,用于数据变更追踪;
可对 name 加索引提升查询效率(如果经常用 name 查询)。
五、完整建议汇总
数据来源字段容错 .get(“字段”, “”).strip(),避免出现空值和空格
唯一键选择 不建议用 name 做 dict key,推荐用 employee_id
字段覆盖 如果同名员工存在,当前写法会覆盖后者数据,应避免
接口容错 加 try-except 捕获 Feishu API 错误
数据变更记录 加入 日志记录(如新增几条,更新几条)
模型增强 建议增加时间戳字段(created_at, updated_at)
配置来源 建议统一放到 settings,例如字段映射配置等,便于维护