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.nameand onself.xyz.bar.textdoes 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 JOINs in the query, to load the related xyz and xyz.bar object in the same query, and thus will result in no extra querying.
