Как использовать "Prefetch()" с "filter()" для сокращения запросов `SELECT` для итерации 3 или более моделей?

У меня есть модели Country, State и City, которые связаны между собой внешними ключами, как показано ниже:

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)

Затем, когда я итерирую модели Country, State и City с помощью prefetch_related() и all(), как показано ниже:

                                  # Here                                  # Here
for country_obj in Country.objects.prefetch_related("state_set__city_set").all():
    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 запросов, как показано ниже. * Я использую PostgreSQL, и ниже показаны журналы запросов PostgreSQL, и вы можете посмотреть этот ответ , объясняющий, как включить и отключить журналы запросов на PostgreSQL:

enter image description here

Но когда я делаю итерацию с помощью filter() вместо all(), как показано ниже:

                                                                          # Here
for country_obj in Country.objects.prefetch_related("state_set__city_set").filter():
    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)
<<<Вместо 3 запросов

выполняются 8 запросов SELECT, как показано ниже:SELECT

enter image description here

Итак, я использую Prefetch() с filter() для сокращения 8 SELECT запросов до 3 SELECT запросов, как показано ниже:

for country_obj in Country.objects.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'
    ),
).filter():
    print(country_obj, country_obj.state_obj, country_obj.city_obj)

Но возникает ошибка, описанная ниже:

AttributeError: Cannot find 'city_set' on Country object, 'city_set' является недопустимым параметром для prefetch_related()

Итак, как я могу использовать Prefetch() с filter() для сокращения 8 запросов SELECT до 3 запросов SELECT?

Попробуйте вложить объекты предварительной выборки

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

Затем запустите все с 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)
Вернуться на верх