在Django循环创建对象时减少重复数据库查询的方法
解决Django导入数据时重复查询数据库的问题
首先纠正代码里的一个关键错误:Purchase模型的item字段外键关联错误,应该指向Item模型而非Customer,修正后模型如下:
class Purchase(models.Model): customer = models.ForeignKey("Customer", on_delete=models.CASCADE) item = models.ForeignKey("Item", on_delete=models.CASCADE) # 修正外键关联对象
针对重复查询的问题,核心方案是提前批量获取所需数据并缓存到字典,循环时直接从内存取值,避免重复访问数据库,具体实现如下:
优化后的导入代码
# 从数据中提取所有不重复的客户名和商品名 customer_names = {entry['customer'] for entry in purchase_table} item_names = {entry['item'] for entry in purchase_table} # 批量查询目标数据,存入字典(键为名称,值为模型对象) customer_cache = {c.name: c for c in Customer.objects.filter(name__in=customer_names)} item_cache = {i.name: i for i in Item.objects.filter(name__in=item_names)} # 批量构造Purchase对象 purchase_objects = [] for entry in purchase_table: customer = customer_cache[entry['customer']] item = item_cache[entry['item']] purchase_objects.append(Purchase(customer=customer, item=item)) # 一次性批量插入数据库,大幅减少IO操作 Purchase.objects.bulk_create(purchase_objects)
为什么之前的Customer.objects.all()无效?
Customer.objects.all()返回的是Django的QuerySet,它采用惰性加载机制——只有在实际访问数据时才会触发数据库查询。调用customers.get(name=xxx)时,并不会从已加载的内存数据中查找,而是重新生成SQL查询数据库,所以无法避免重复查询。而字典缓存的方式是将查询结果一次性加载到内存,后续直接从字典取值,完全不会触发新的数据库请求。
另外,使用bulk_create代替循环调用save(),能将多次插入操作合并为一次数据库请求,在数据量较大时能显著提升导入效率。
内容的提问来源于stack exchange,提问作者Andrew Plowright
相关产品推荐
相关产品推荐

