Django在循环查询集时,db读取何时发生?

时间:2018-04-15 15:03:06

标签: django sqlite for-loop django-orm

我循环访问我的数据库并更新所有Company个对象。

for company in Company.objects.filter(updated=False):
    driver.get(company.company_url)
    company.adress = driver.find_element_by_id("address").text
    company.visited = True    
    company.save()

我的问题是它花了太长时间,所以我想运行这个相同代码的另一个实例,但我很好奇实际的数据库读取是否发生。如果在此循环运行时company.visited变为True,此循环仍会访问该变量吗?如果我为visited添加了第二张支票怎么办?如果第一个实例不能识别第二个实例的工作,我不想启动第二个循环:

for company in Company.objects.filter(updated=False):
    if company.visited:
        continue
    driver.get(company.company_url)
    company.adress = driver.find_element_by_id("address").text
    company.visited = True    
    company.save()

1 个答案:

答案 0 :(得分:1)

Company.objects.filter(updated=False)转换为普通的SQL查询:

SELECT * FROM appName_company WHERE updated is false

当您开始遍历Company个对象时,将执行此SQL查询。它只执行一次。第二个服务器将无法识别第一个服务器的工作,因为它们都将通过相同的Company对象。

使用原子事务和select_for_update()锁定行以避免竞争条件:

from django.db import transaction

for company in Company.objects.filter(updated=False):
    with transaction.atomic():
        Company.objects.select_for_update().get(id=company.id)
        if company.visited:
            continue
        driver.get(company.company_url)
        company.adress = driver.find_element_by_id("address").text
        company.visited = True    
        company.save()

您可以在多台服务器上运行此代码。每个Company只会被处理一次。

如果您需要定期执行此代码,我强烈建议您使用Celery。每个公司调度一个任务,让多个工作人员并行完成工作:

from celery import shared_task

@shared_task
def dispatch_tasks():
    for company in Company.objects.filter(updated=False):
        process_company.delay(company.id)

@shared_task
@transaction.atomic
def process_company(company_id):
    company = Company.objects.select_for_update().get(id=company_id)
    if company.visited:
        continue
    driver.get(company.company_url)
    company.adress = driver.find_element_by_id("address").text
    company.visited = True    
    company.save()

编辑:哦,我看到你用sqlite标签标记了这个问题。我建议切换到PostgreSQL,因为SQLite在并发方面非常糟糕。我的回答应该与SQlite一起使用,但是锁可能会减慢数据库的速度。