There are many opportunities for improvement. Using ManyToManyField, you can explicitly define a connection table, which is convenient to consider as one visit to the city during a particular trip. During this visit, we had events, so activities should have a visit in the opposite direction.
For each foreign key in the table, Django will add an API convenience manager for sets of objects on the opposite side of the relationship. Destination will have a visit_set , but there will be a Trip . Similarly, due to visit foreignkey in Activity each visit will have activity_set .
Start with the models first:
from django.db import models
Then modify list_trip bit, add print_trip to clarify what happens in the template:
def list_trip(request, template_name = 'trip-list.html'): return render_to_response(template_name, { 'page_title': 'List of trips', 'trips': Trip.objects.all(), }) def print_trips(): for trip in Trip.objects.all(): for visit in trip.visit_set.select_related().all(): print trip.id, '-', visit.destination.city_name for act in visit.activity_set.all(): print act.name
And finally, an improved template:
{% block content %} {% for trip in trips %} {{ trip.id }} - {{ trip.name }} {% for visit in trip.visit_set.select_related.all %} {{ visit.destination.city_name }} {% for act in visit.activity_set.all %} {{ act.name }} {% endfor %} {% endfor %} {% endfor %} {% endblock %}
There are few more options for improving performance. Notice I used select_related. This will prefetch all destinations during visits, so visit.destination.city_name will not make another db call. However, this does not work for ManyToMany inverse relationships (in our case, all activity_set members). Django 1.4 will come out with a new method called prefetch_related, which will also solve this.
At the same time, read Effective Reverse Lookup to understand how to further reduce the number of database calls. The comments also mention several available solutions.