Sometimes it makes sense to use select_related in a django template. For example, say I have a class extending DetailView
class DemoCarView(DetailView):
model = Car
Based on the following contrived model
# Cars
class Car(models.Model):
name = models.CharField(max_length=32)
# Manufacturers
class Manufacturer(models.Model):
name = models.CharField(max_length=32)
# Parts
class Part(models.Model):
name = models.CharField(max_length=32)
car = models.ForeignKey(Car)
manufacturer = models.ForeignKey(Manufacturer)
The html template is then
{{ car.name }}
<ul>
{% for part in car.part_set.all %}
<li>{{ part.name }} - {{ part.manufacturer.name }} </li>
{% endfor %}
</ul>
This works perfectly to get the car, the parts that comprise it, and the manufacturers of those parts. However, this will use 2+number_of_parts SQL queries to do so. Easily fixed like so:
{{ car.name }}
<ul>
{% for part in car.part_set.select_related.all %}
<li>{{ part.name }} - {{ part.manufacturer.name }} </li>
{% endfor %}
</ul>
Now the optimum of 2 queries is run. However, select_related
is joining the Parts with every Foreign Key that it has. Is there a way to restrict this to just the desired related tables. In Python it's just:
Part.objects.select_related('manufacturer').filter(car=car)
Can this be done in the template?
Note: I know I can do this in the view very easily by returning a context for 'car' and one for 'parts' with select_related('manufacturer')
on the filter, but it's quite a bit more code compared to the DetailView sub-class I've used above. Something like this:
class DemoCarViewPreload(TemplateView):
template_name = 'demo/car_detail_preload.html'
def get_context_data(self, **kwargs):
context = super(DemoCarViewPreload, self).get_context_data(**kwargs)
car = Car.objects.get(pk=kwargs.get('pk'))
context['car'] = car
context['parts'] = Part.objects.select_related('manufacturer').filter(car=car)
return context
However, this requires the template to be more specific to this view as it will now need to use the 'parts' context rather than car.part_set.all
. Also, it's simply more work to make this view in the first place.
How about a simple method on the Car
model?
class Car(models.Model):
...
def parts_with_manufacturers(self):
return self.part_set.select_related('manufacturer')
and then
{% for part in car.parts_with_manufacturers %}
<li>{{ part.name }} - {{ part.manufacturer.name }} </li>
{% endfor %}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With