I've searched in several places but I haven't found a satisfactory answer anywhere.
I have these 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
My first question is, on self.xyz.bar.name
and on self.xyz.bar.text
does hit in the database?
If yes, how can i optimize it?
I already tried this, but I'm not sure if it improves anything:
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
And here comes my second question, using select_related()
, I would create a single method like Sigleton pattern, something like this:
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
CodePudding user response:
My first question is, on
self.xyz.bar.name
and onself.xyz.bar.text
does hit in the database?
Yes, given these are not loaded yet by some other query. If you query for self.xyz.bar.name
, and xyz
is not loaded yet, it will make two queries: one to fetch the xyz
object, and one to fetch the bar
object referenced by that xyz
object.
If you thus access both .bar_name()
and .bar_text()
, it will not make four queries, since the data is loaded by the first method call, and the second one will "piggy back" on the work already done by the first one.
If yes, how can [I] optimize it?
I would advise not to optimize the queries in the methods, since here you will each time make the same query, as a result with your second approacy, if you call .bar_name()
twice, it will two times make the same query. You simply should load the Foo
object efficiently. Indeed, you can obtain the foo
object with:
foo = Foo.objects.select_related('xyz__bar').get(pk=42)
This will make LEFT OUTER JOIN
s in the query, to load the related xyz
and xyz.bar
object in the same query, and thus will result in no extra querying.