Как использовать "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:
Но когда я делаю итерацию с помощью 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
Итак, я использую 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)