假设我有model
,比如:
class User(AbstractUser):
...
seller = models.ForeignKey(Seller, related_name="user", on_delete=models.SET_NULL, null=True)
...
我试图使用以下代码获取seller
电子邮件地址:
from app.models import User
from django_print_sql import print_sql
with print_sql(count_only=False):
users = User.objects.filter(is_active=True, seller_id__isnull=False).select_related().only('seller__email')
for u in users.iterator():
email = u.seller.email
send_email(email)
在这种情况下,我可以看到SQL查询,如:
SELECT `user`.`id`,
`user`.`seller_id`
FROM `user`
WHERE (`user`.`is_active`
AND `user`.`seller_id` IS NOT NULL)
...
SELECT `seller`.`id`,
...
`seller`.`email`,
...
FROM `seller`
WHERE `seller`.`id` = 1
...
问题是Django ORM在每次迭代(Select seller... where seller.id = ...
(时访问DB。因此,如果我们有很多卖家,那将是太多的查询(==DB连接(。
换句话说,可以用values
:代替only
from app.models import User
from django_print_sql import print_sql
with print_sql(count_only=False):
users = User.objects.filter(is_active=True, seller_id__isnull=False).select_related().values('seller__email')
for u in users.iterator():
email = u['seller__email']
send_email(email)
我可以看到SQL查询,比如:
SELECT `seller`.`email`
FROM `user`
INNER JOIN `seller` ON (`user`.`seller_id` = `seller`.`id`)
WHERE (`user`.`is_active`
AND `user`.`seller_id` IS NOT NULL)
它稍微好一点,我们可以通过单个DB查询来获取电子邮件,但iterator
在这里是无用的,因为我们在dict
对象中加载所有电子邮件一次,整个数据都存在于内存中。
问题是
在不创建限制User.objects.filter
查询计数的手动子循环的情况下,是否可以按数据块进行迭代(比如按500封电子邮件/单个查询(?换句话说,在这种情况下,什么是最有效的迭代器?
在您的情况下,您没有为select_related
提供参数,我建议您将seller
字段设置为select_related
,如下所示:
from app.models import User
from django_print_sql import print_sql
with print_sql(count_only=False):
users = User.objects.select_related('seller').filter(is_active=True, seller_id__isnull=False).only('seller__email')
for u in users.iterator():
email = u.seller.email
send_email(email)
将CCD_ 12置于CCD_ 13管理器之前。通过这样做,django将卖家模型记录作为每个迭代用户的字段,并且不会在每次迭代中访问数据库。
看起来select_related
适用于这种情况:
from app.models import User
from django_print_sql import print_sql
with print_sql(count_only=False):
users = User.objects.filter(is_active=True, seller_id__isnull=False).select_related('seller').only('seller__email').exclude(seller__email__exact='')
for u in users.iterator():
send_email(u.seller.email)
SELECT `user`.`id`,
`user`.`owner_id`,
`seller`.`id`,
`seller`.`email`
FROM `user`
INNER JOIN `seller` ON (`user`.`seller_id` = `seller`.`id`)
WHERE (`user`.`is_active`
AND `user`.`seller_id` IS NOT NULL
AND NOT (`seller`.`email` =
AND `seller`.`email` IS NOT NULL))