Обращается ли свойство Django is models к базе данных более одного раза?

Я искал в нескольких местах, но нигде не нашел удовлетворительного ответа.

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

App bar

    class Bar(models.Model):
        name = models.CharField(max_lenth=50)
        text = models.TextField()

App xyz

    class Xyz(models.Model):
        bar = models.ForeingKey(Bar, on_delete=models.CASCADE, verbose_name='Bar')

App foo

    class Foo(models.Model):
        xyz = models.OneToOneField(Xyz, on_delete=models.CASCADE, verbose_name='Xyz')
        
        def bar_name(self):
            return self.xyz.bar.name

        def bar_text(self):
            return self.xyz.bar.text

Мой первый вопрос, на self.xyz.bar.name и на self.xyz.bar.text попадает ли в базу данных?

Если да, то как я могу его оптимизировать?


Я уже пробовал это, но не уверен, улучшает ли это что-нибудь:

    def bar_name(self):
        return Xyz.objects.select_related('bar').get(pk=self.xyz_id).bar.name

    def bar_text(self):
        return Xyz.objects.select_related('bar').get(pk=self.xyz_id).bar.text

А вот и мой второй вопрос, используя select_related(), я бы создал один метод, как в паттерне Sigleton, примерно так:

    def singleton_bar(self):
        return Xyz.objects.select_related('bar').get(pk=self.xyz_id)

    def bar_name(self):
        return self.singleton_bar().bar.name

    def bar_text(self):
        return self.singleton_bar().bar.text

Мой первый вопрос, на self.xyz.bar.name и на self.xyz.bar.text попадает ли в базу данных?

Да, если они еще не загружены каким-то другим запросом. Если вы запрашиваете self.xyz.bar.name, а xyz еще не загружен, будет сделано два запроса: один для получения объекта xyz и один для получения объекта bar, на который ссылается этот объект xyz.

Если вы таким образом обратитесь и к .bar_name(), и к .bar_text(), то не сделаете четыре запроса, так как данные загружены первым вызовом метода, а второй будет "копировать" работу, уже проделанную первым.

Если да, то как [я] могу его оптимизировать?

Я бы посоветовал не оптимизировать запросы в методах, так как здесь вы каждый раз будете делать один и тот же запрос, в результате при вашем втором приближении, если вы вызовете .bar_name() дважды, он два раза сделает один и тот же запрос. Вы просто должны эффективно загрузить объект Foo. Действительно, вы можете получить объект foo с помощью:

foo = Foo.objects.select_related('xyz__bar').get(pk=42)

Это сделает LEFT OUTER JOIN в запросе, чтобы загрузить связанные xyz и xyz.bar объекты в том же запросе, и таким образом не приведет к лишним запросам.

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