简体   繁体   English

如何使用“Prefetch()”和“filter()”来减少“SELECT”查询以迭代 3 个或更多模型?

[英]How to use "Prefetch()" with "filter()" to reduce `SELECT` queries to iterate 3 or more models?

I have Country , State and City models which are chained by foreign keys as shown below:我有CountryStateCity模型,它们由外键链接,如下所示:

class Country(models.Model):
    name = models.CharField(max_length=20)

class State(models.Model):
    country = models.ForeignKey(Country, on_delete=models.CASCADE)
    name = models.CharField(max_length=20)
    
class City(models.Model):
    state = models.ForeignKey(State, on_delete=models.CASCADE)
    name = models.CharField(max_length=20)

Then, when I iterate Country , State and City models with prefetch_related() and all() as shown below:然后,当我使用prefetch_related()all()迭代CountryStateCity模型时,如下所示:

                                  # ↓ Here ↓
for country_obj in Country.objects.all().prefetch_related("state_set__city_set"):
    for state_obj in country_obj.state_set.all(): # Here
        for city_obj in state_obj.city_set.all(): # Here
            print(country_obj, state_obj, city_obj)

3 SELECT queries are run as shown below. 3 SELECT查询运行如下所示。 *I use PostgreSQL and these below are the query logs of PostgreSQL and you can see this answer explaining how to enable and disable the query logs on PostgreSQL: *我使用 PostgreSQL 以下是 PostgreSQL 的查询日志,你可以看到这个解释如何启用和禁用 PostgreSQL 的查询日志的答案:

在此处输入图像描述

But, when I iterate with filter() instead of all() as shown below:但是,当我使用filter()而不是all()进行迭代时,如下所示:

                                  # Here
for country_obj in Country.objects.filter().prefetch_related("state_set__city_set"):
    for state_obj in country_obj.state_set.filter(): # Here
        for city_obj in state_obj.city_set.filter(): # Here
            print(country_obj, state_obj, city_obj)

8 SELECT queries are run as shown below instead of 3 SELECT queries: 8 SELECT查询运行如下所示,而不是 3 SELECT查询:

在此处输入图像描述

So, I use Prefetch() with filter() to reduce 8 SELECT queries to 3 SELECT queries as shown below:因此,我使用Prefetch()filter()将 8 个SELECT查询减少到 3 个SELECT查询,如下所示:

for country_obj in Country.objects.filter().prefetch_related(
    Prefetch('state_set', # Here
        queryset=State.objects.filter(),
        to_attr='state_obj'
    ),
    Prefetch('city_set', # Here
        queryset=City.objects.filter(),
        to_attr='city_obj'
    ),
):
    print(country_obj, country_obj.state_obj, country_obj.city_obj)

But, the error below occurs:但是,出现以下错误:

AttributeError: Cannot find 'city_set' on Country object, 'city_set' is an invalid parameter to prefetch_related() AttributeError:在国家 object 上找不到“city_set”,“city_set”是 prefetch_related() 的无效参数

So, how can I use Prefetch() with filter() to reduce 8 SELECT queries to 3 SELECT queries?那么,我如何使用Prefetch()filter()将 8 个SELECT查询减少到 3 个SELECT查询?

Try nesting prefetch objects尝试嵌套预取对象

countries = Country.objects.filter().prefetch_related(
    Prefetch('state_set', # Here
        queryset=State.objects.filter().prefetch_related(
            Prefetch("city_set",
            queryset=City.objects.filter(),
        ),
    ),
)

Then run everything with all() since it's already filtered然后使用all()运行所有内容,因为它已经被过滤

for country_obj in countries:
    for state_obj in country_obj.state_set.all():
        for city_obj in state_obj.city_set.all():
            print(country_obj, state_obj, city_obj)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM