Обращается ли свойство 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
объекты в том же запросе, и таким образом не приведет к лишним запросам.