飞书配置表数据同步到数据库中

这是我的从飞书取数据的代码

python 复制代码
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 = None
    hasMore = True
    employees = {}

    while hasMore:
        staff_data = bitable_service.fetch(app_token, table_id, page_token=page_token)
        # 格式参考warehouse.json
        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"]] = employee

        if not hasMore:
            break
        page_token = staff_data["data"].get("page_token")

    return employees

这是我的数据库模型

python 复制代码
from django.db import models


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

这是我将飞书数据进入数据库的代码

python 复制代码
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 的记录,防止插入空主键。

🔍建议优化点

  1. 字段容错性检查
    在访问字段时使用了 fields.get(),但在 position、department、employee_id 字段不是所有记录都存在时,可能得到 None。可以在构建 employee 字典时加默认值,避免报错或数据不一致:
python 复制代码
employee = {
    "employee_id": fields.get("工号", "").strip(),
    "name": fields.get("姓名", "").strip(),
    "position": fields.get("人员.职务", "").strip(),
    "department": fields.get("部门名", "").strip(),
}

注意 .strip() 可避免导入数据库中的空格异常。

  1. 避免空姓名造成 key 重复覆盖
    使用了 employees[employee["name"]] = employee,这会以"姓名"作为 key,而如果出现多个同名员工,可能被覆盖,造成数据丢失。

建议改为用 employee_id 做 key,更稳妥:

python 复制代码
employees[employee["employee_id"]] = employee
  1. 字段命名检查

    使用的是字段名如 "人员.职务",这是合法的,但建议在飞书中避免这种带点的字段名,因为它可能与 Python 字段语义混淆,或者将来你要做统一映射时会麻烦。

  2. 同步逻辑建议加日志(可选)

    可以加一点日志输出,方便调试或上线监控:

python 复制代码
import logging
logger = logging.getLogger(__name__)

def sync_employee_to_db():
    data_dict = get_employee_from_feishu()
    created, updated, skipped = 0, 0, 0

    for data in data_dict.values():
        if not data["employee_id"]:
            skipped += 1
            continue

        obj, 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 += 1
        else:
            updated += 1

    logger.info(f"员工同步完成:新增 {created} 条,更新 {updated} 条,跳过 {skipped} 条")

✅ 总结

飞书分页抓取

空值处理

⚠️ 建议加默认值 .get(..., "").strip()

主键唯一性

⚠️ 建议用 employee_id 做 key

数据同步方式

✅ update_or_create 正确

日志与监控

⚠️ 建议加简单日志

字段命名规范性

⚠️ "人员.职务" 命名不够语义清晰,建议优化

下面是这段同步员工数据代码的详细结构分析,包括每一部分的职责、依赖关系、潜在问题和优化建议。

一、函数入口:sync_employee_to_db()

python 复制代码
def sync_employee_to_db():

作用:

主函数,负责执行"从飞书多维表格获取员工数据 → 写入本地数据库"的全过程。

二、从飞书获取数据

python 复制代码
data_dict = get_employee_from_feishu()

对应的函数:

python 复制代码
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 = None
    hasMore = True
    employees = {}

    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"]] = employee

        if not hasMore:
            break
        page_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);

三、同步到数据库

python 复制代码
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

python 复制代码
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,例如字段映射配置等,便于维护

相关推荐
2301_802502331 小时前
哈工大计算机系统2025大作业——Hello的程序人生
数据库·程序人生·课程设计
Alan3165 小时前
Qt 中,设置事件过滤器(Event Filter)的方式
java·开发语言·数据库
TDengine (老段)6 小时前
TDengine 集群容错与灾备
大数据·运维·数据库·oracle·时序数据库·tdengine·涛思数据
Lao A(zhou liang)的菜园6 小时前
高效DBA的日常运维主题沙龙
运维·数据库·dba
迪迦不喝可乐7 小时前
mysql知识点
数据库·mysql
不太可爱的大白7 小时前
MySQL 事务的 ACID 四大特性及其实现原理
数据库·mysql
观测云8 小时前
HikariCP 可观测性最佳实践
数据库
文牧之9 小时前
PostgreSQL的扩展 dblink
运维·数据库·postgresql
趁你还年轻_9 小时前
Redis-旁路缓存策略详解
数据库·redis·缓存
在云上(oncloudai)10 小时前
AWS DocumentDB vs MongoDB:数据库的技术抉择
数据库·mongodb·aws