title: combine number of database requests using ORM
body: <p>I am working on a <a href="http://5aus55.info" rel="nofollow">website</a>, where a user can vote and comment on different projects in various categories. Whenever a user returns to the site, their votes will be highlighted and all the comments will be visible.
  The problem is that every time the page is loaded there are many database calls due to the design of my models or due to me not finding an efficient way of retrieving the data.</p>
  
  <p>My models are as follows:</p>
  
  <pre><code>class ProjectCategory:
      title=models.CharField(...)
  
  class Project:
      category=models.ForeignKey(ProjectCategory)
      title=models.CharField(...)
  
      def ownedComments(self):
              return Comment.objects.filter(project=self).order_by('-submissionTime')
  
      def ownedCommentsPreview(self):
              return Comment.objects.filter(project=self).order_by('-submissionTime')[0:3]
  
      def ownedVotes(self):
              return Vote.objects.filter(project=self,upVoted=True).count() 
  
  
  class Vote:
      project=models.ForeignKey(Project)
      user=models.ForeignKey(User)
      vote=models.IntegerField(...)
  
  class Comment:
      project=models.ForeignKey(Project)
      user=models.ForeignKey(User)
      title=models.CharField(...)    
  </code></pre>
  
  <p>Here are relevant parts of my <strong>views.py</strong>:</p>
  
  <pre><code>def getCurrentUserData(request=None):
          if (request==None) or (request.user.is_authenticated()==False):
                  return {'loggedInUsername':'Anonym','userIsLoggedIn':False}
          return {'loggedInUsername':request.user.username,'userIsLoggedIn':True}
  
  
  def getProjectResponse(request,surveyFile=None,additionalInfo={}):
          userVotedUp=[]
          if surveyFile!=None:
                  userVotes=Vote.objects.filter(upVoted=True,surveyFile=surveyFile)
                  for vote in userVotes:
                          userVotedUp.append(vote.project.id)
          answerDict={'userVotedUp':userVotedUp,'prjCategories':PrjCategory.objects.select_related(),'projects': Project.objects.prefetch_related('category').filter(visible=True),'spammerquestion':spammerquestion.SpammerQuestion().getRandomQuestion(),'rotatingLines':helpers.getRandomRotatingLines(),'backgroundimageurl':helpers.getBackgroundImageUrl()}
          answerDict.update(getCurrentUserData(request))
          answerDict.update(additionalInfo)
          return answerDict
  
  def showProject(request,prjNo):
          currentPrj=get_object_or_404(Project,prjNo=prjNo,visible=True)
          surveyFile=assignSurveyFile(request,create_surveyFile_if_not_existing=False)        
          return render_to_response('website/singleproject.html', getProjectResponse(request,surveyFile,{'currentPrj':currentPrj,'immediateProjectID':currentPrj.id}),context_instance=RequestContext(request))
  </code></pre>
  
  <p>The template contains many iterations over projects and categories, there might be several database calls happening here. Relevant parts of the <strong>template</strong> are:</p>
  
  <pre><code>{% for project in projects %}
      self.vote['{{ project.id }}']=ko.observable(new Vote({'totalVoteNumber':ko.observable({{ project.ownedVotes }}),'userVoteState':ko.observable({% if project.id in userVotedUp %}true{% else %}false{% endif %})}));
      self.comments['{{ project.id }}']=ko.observableArray([]);
      {% for comment in project.ownedCommentsPreview %}
          self.comments['{{ project.id }}'].push(new Comment({'description':'{{ comment.description|striptags|safe }}','submissionTime':'{{ comment.submissionTime|humanDT }}','commentUuid':'{{ comment.commentUuid }}','deletable':false}));
      {% endfor %}
  {% endfor %}
  
  
          {% for prjCat in prjCategories %}
                  .prjCatUnderline{{ prjCat.id }} {border-bottom: 0px solid {{ prjCat.color }};}
                  .prjCatUnderline{{ prjCat.id }}:hover {border-bottom: 3px solid {{ prjCat.color }};}
          {% endfor %}
  
  
          {% for prjCat in prjCategories %}
                  &lt;a href="#" class="prjCatUnderline{{ prjCat.id }}" data-filter=".prjCat{{ prjCat.id }}"&gt;{{ prjCat.title }}&lt;/a&gt;
                  {% if not forloop.last %}&lt;span class="catpipe"&gt;|&lt;/span&gt;{% endif %}
          {% endfor %}
  
          &lt;script type="text/javascript"&gt;
                  var projectLocations = [{% for project in projects %}{% if project.location != '' %}['{{ project.title }}', {{ project.location }}, {{ project.id }}, '{{ MEDIA_URL }}{{ project.mapImage }}', {{ project.mapImage.width }}, {{ project.mapImage.height }}, '{{ project.id }}'],
                  {% endif %}{% endfor %}];
          &lt;/script&gt;
  
  
          {% for project in projects %}
                  &lt;div class="item prjCat{{ project.category.id }}"&gt;
                          &lt;div class="prjnumberbox"&gt;
                                  &lt;div class="numberbar" style="background-color:{{ project.category.color }}"&gt;&lt;/div&gt;
                                  &lt;div class="prjnumber"&gt;{{ project.prjNo|stringformat:"02d" }}&lt;/div&gt;
                                  &lt;div class="numberbar" style="background-color: {{ project.category.color }}"&gt;&lt;/div&gt;
                          &lt;/div&gt;
                          &lt;div class="prjheadline"&gt;&lt;span&gt;{{ project.title }}&lt;/span&gt;&lt;/div&gt;
                          &lt;div class="prjimagecontainer"&gt;
                                  &lt;!-- ko with: $root.vote['{{ project.id }}'] --&gt;
                                  &lt;div class="prjimageoverlay"&gt;&lt;a data-bind="click: function(data,event) { $root.showSingleProjectData('{{ project.id }}',data) },css : { imgVoted :userVoteState }" class="singleProjectOverlay" href="{% url 'website.views.showProject' prjNo=project.prjNo %}"&gt;&lt;/a&gt;&lt;/div&gt;
                                  &lt;div class="prjimage"&gt;&lt;img src="{{ MEDIA_URL }}{{ project.teaserImage }}" /&gt;&lt;/div&gt;
                                  &lt;!-- /ko --&gt;
                          &lt;/div&gt;
                          &lt;div class="prjtextcontainer" {% if project.ownedComments|length_is:"0" %}style="background-image:none;"{% endif %}&gt;
                                  &lt;div class="prjtext"&gt;{{ project.teaserText|safe }} &lt;a class="singleProjectOverlay" data-bind="click: function(data,event) { showSingleProjectData('{{ project.id }}',data) }" href="{% url 'website.views.showProject' prjNo=project.prjNo %}"&gt;mehr &amp;gt;&amp;gt;&lt;/a&gt;&lt;/div&gt;
                                  &lt;div class="votesline"&gt;&lt;span class="voteslinefont" data-bind="with: vote['{{ project.id }}']"&gt;&amp;nbsp;&lt;span class="votecount" data-bind="text: totalVoteNumber"&gt;{{ project.ownedVotes }}&lt;/span&gt; votes&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
  
                                  &lt;div class="prjcomments nojs"&gt;
                                  {% for comment in project.ownedCommentsPreview|slice:":4" %}
                                          &lt;div class="prjcommentitem"&gt;
                                                  {{ comment.teaserdescription }}
                                          &lt;/div&gt;
                                  {% endfor %}
                                  &lt;/div&gt;
                                  &lt;div class="prjcomments" data-bind="foreach: comments['{{ project.id }}'], visible: comments['{{ project.id }}']().length &gt; 0"&gt;
                                          &lt;!-- ko if: $index() &lt; 4 --&gt;
                                          &lt;div class="prjcommentitem"&gt;
                                                  &lt;em data-bind="text: submissionTime"&gt;&amp;nbsp;&lt;/em&gt;&lt;br/&gt;
                                                  &lt;span data-bind="text: description"&gt;&amp;nbsp;&lt;/span&gt;
                                                  &lt;a href="#" data-bind="click: function(data, event) { $parent.removeComment(data,'{{ project.id }}', data,event) }, visible: deletable"&gt;[X]&lt;/a&gt;
                                          &lt;/div&gt;
                                          &lt;!-- /ko --&gt;
  
                                  &lt;/div&gt;
                          &lt;/div&gt;
                  &lt;/div&gt;
          {% endfor %}
  </code></pre>
  
  <p>For the front page I'd like to retrieve all the projects, most recent three comments, the total number of votes for a project and the comments and votes by the user.
  It would be great if that was possible with one db call, or with two, a general one to get the number of votes and the comments, and one for the data specific to the user.</p>
  
  <p>CUrrently I am retrieving all the projects with one call, so I am automatically getting all the project categories. Then I get all the comments at once and assign them to the projects in python. I then get the comments and votes for a certain user with one call each.</p>
  
  <p>Is there a way - without using sql directly - to get</p>
  
  <ul>
  <li>comments and votes for a user with a single database call</li>
  <li>comments, projects, accumulated number of votes per project and project categories at once?</li>
  </ul>
  
  <p>One idea would be to download the whole database using select_related() and then sorting everything out in Python, but if in the future I have a large number of votes or comments, this might not be a good idea.</p>