Возможно ли здесь избежать n+1 запроса в django? Если да, то как?

У меня есть эти модели

class Thing1(MyModel):
    thing2 = models.OneToOneField(
        Thing2, on_delete=models.PROTECT, related_name="super_pack"
    )
    some_id = models.IntegerField()


class Thing2(MyModel):
    name = models.CharField(max_length=50, primary_key=True)

class Thing3(MyModel):
    name = models.CharField(max_length=255)
    thing2 = models.ForeignKey(
        Thing2,
        related_name="thing3s",
    )

class Thing4(MyModel):
    root_thing3 = models.OneToOneField(
        Thing3, on_delete=models.PROTECT, related_name="my_related_name"
    )
    member_item_thing3s = models.ManyToManyField(
        Thing3,
        through="unimportant",
        related_name="important_related_name",
    )
    is_default = models.BooleanField(default=False)

Я работаю с сериализатором Django. У меня уже определен queryset с предварительной выборкой.

В существующем виде, следующее выполняется:

all_thing1s: QuerySet[Thing1]
for thing1 in all_thing1s:
    first_thing3 = Thing1.thing2.thing3s.all()[0]

(далее предполагается, что мы находимся в теле цикла)

что не идеально, если всегда есть 1 thing3 схема не должна позволять много, но я не могу изменить схему в настоящее время.)

На данный момент я считаю, что возможность дальнейшей предварительной выборки была нарушена, поскольку мы вернули Thing3 (first_thing3) и больше не имеем набора запросов для работы В начале этого я думал, что Django магически использует предыдущие префетчи, я не понимал, что фильтры и т.д., которые вы хотите использовать префетчи, должны быть привязаны к префетчам.

Но теперь я хочу сделать:

thing4 = first_thing3.important_related_name.all()[0]
return thing4.root_thing3

(примечание: first_thing3 и root_thing3 имеют один и тот же тип, но не являются одним и тем же. И да, опять то же глупое предположение, что существует единственное thing4, связанное с first_thing3)

Но согласно жирному тексту, я считаю, что предварительная выборка на кверисете представления невозможна.

Я подумала, что, возможно, ты мог бы быть таким

first_thing3 = Thing1.thing2.thing3s.all()[0]
query_set = Thing1.thing2.thing3s.filter(id=first_thing.id)
query_set.important_related_name[0]
...

Но нет, потому что добавление фильтра должно быть учтено в исходной предварительной выборке, что невозможно, а последующая important_related_name имеет смысл только в том случае, если она все равно относится к одному элементу.

Итак, это было около 8 часов открытий для меня. Думаю, я определил, что здесь невозможна предварительная выборка с помощью Django и что n+1 неизбежно.

Может ли кверисет быть агрегатом?

Я хочу подтвердить, что избежать n+1 невозможно (используя встроенную функциональность Django).

Вернуться на верх