bulk_create比for循环快得多是因为它将多次insert合并为单条(或少数几条)sql语句,跳过信号、验证、auto_now填充等orm开销,实测快5–20倍;但需手动设置auto_now/add和default字段值,并必须指定batch_size以防sql超长报错。

bulk_create 为什么比 for 循环插入快得多
因为 bulk_create 把多个模型实例一次性组装成单条(或少数几条)INSERT INTO ... VALUES (...), (...), (...) SQL 语句发给数据库,绕过了 Django ORM 默认的逐条 save() 流程——后者会触发信号、验证、默认值填充、外键预加载等开销。实测插入 10k 条数据,bulk_create 通常比循环 save() 快 5–20 倍,取决于字段复杂度和数据库类型。
但注意:它不调用 save() 方法,所以 pre_save/post_save 信号不会触发,auto_now/auto_now_add 字段也不会自动填充,default 函数也不会执行。
必须手动设置 auto\_now 和 default 字段值
如果你的模型有 created_at = models.DateTimeField(auto_now_add=True) 或 status = models.CharField(default='pending'),bulk_create 不会帮你填。漏掉会导致数据库报错(NOT NULL constraint failed)或存入 NULL。
- 显式传入当前时间:
MyModel(created_at=timezone.now(), ...) - 对函数型
default,要手动调用:status=generate_status(),而不是留空或传None - 如果字段有
blank=True, null=True且你确实想留空,就明确传None,别依赖 ORM 自动处理
batch_size 参数不是可选,而是必调参数
PostgreSQL 和 MySQL 对单条 INSERT 的值列表长度有限制(例如 PostgreSQL 默认 65535 个参数)。不设 batch_size,插入上万条时大概率遇到 psycopg2.DataError: string is too long 或 MySQLdb._exceptions.OperationalError: (1390, "Prepared statement contains too many placeholders")。
建议按数据库类型设:
- PostgreSQL:
batch_size=1000安全,5000可试,超过10000易触发参数上限 - MySQL:
batch_size=500–2000更稳妥,尤其字段多或含 TEXT 类型时 - SQLite:严格限制在
batch_size=100以内,否则报sqlite3.OperationalError: too many SQL variables
示例:MyModel.objects.bulk_create(objs, batch_size=1000)
忽略重复记录?bulk_create 本身不支持 on conflict
bulk_create 没有内置类似 PostgreSQL ON CONFLICT DO NOTHING 的能力。遇到唯一约束冲突(如 IntegrityError: duplicate key value violates unique constraint),整个批次都会失败,已插入的数据也不会回滚(Django 不自动开启事务包裹 bulk_create)。
解决方式只有两个现实选择:
- 自己先用
values_list('field', flat=True)查出已存在主键/唯一字段值,过滤掉重复项再插入 - 改用原生 SQL(如 PostgreSQL 的
INSERT ... ON CONFLICT)或第三方库如django-postgres-extra(仅限 PG) - 手动加事务 + try/except 捕获
IntegrityError,但无法细粒度跳过单条失败项,只能整批放弃
别指望 ignore_conflicts=True —— 这是 Django 4.1+ 才引入的参数,且仅对 PostgreSQL 和 SQLite 有效,MySQL 仍不支持。
真正批量写入场景里,数据去重和冲突处理往往比插入本身更花时间。别只盯着 bulk_create 这一行代码。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











