Django中批量创建含ForeignKey的Bar实例,当前方法是否最优?
问题解答
模型定义
class Foo(models.Model): name = models.CharField(max_length=255) # 其他字段... class Bar(models.Model): foo = models.ForeignKey(Foo, on_delete=models.CASCADE) # 其他字段...
当前方案的性能问题
你现在这种循环逐个查询Foo ID的方式绝对不是最快的——每一次get都会发起一次数据库请求,要是names列表很长,就会触发N次查询,典型的N+1性能坑,数据量越大越拖慢速度。
最优优化方案:批量查询+映射
最快的做法是先一次性查出所有存在的Foo,把name和ID做个映射字典,再批量创建Bar,全程只需要1次数据库查询:
# 批量获取所有在names列表里的Foo的name和id existing_foos = Foo.objects.filter(name__in=names).values_list('name', 'id') # 转成字典,方便快速查找对应ID name_to_foo_id = {name: foo_id for name, foo_id in existing_foos} to_create = [] for name in names: foo_id = name_to_foo_id.get(name) if foo_id: # 用foo_id直接赋值外键字段,比实例化Foo对象更高效 to_create.append(Bar(foo_id=foo_id, **other_fields)) Bar.objects.bulk_create(to_create)
能不能直接用Foo的name创建Bar?
Django本身没有直接通过外键关联模型的name字段创建实例的语法,但可以通过子查询间接实现,不过批量场景下不如上面的映射方案直观高效。比如单个实例可以这么写:
# 单个实例场景,通过get获取Foo后关联 Bar.objects.create(foo=Foo.objects.get(name="xxx"), **other_fields)
但批量创建时,还是先批量查询做映射的方式性能最优,能避免重复查询的开销。
另外提个小建议:如果Foo的name字段是业务上唯一的,记得给它加unique=True约束和数据库索引,这样批量查询的速度会更快。
内容的提问来源于stack exchange,提问作者codego
相关产品推荐
相关产品推荐

