I'm having an issue where my templates are taking a really long time
to load, and it looks like the problem is that a lot of database calls
are made inside the template.  This seems like it would be easy enough
to fix, but I have kind of an odd setup with my model relationships
that seems to be adding extra complexity.

The application is more or less a blog with multiple types of posts--
articles, book reviews, etc.  Each of the post-type models is
subclassed from a base model called Node.  The base class contains the
fields for things like the node title, author, creation date, and
comment FKs, where the subclass contains the fields for things
specific to the post type--body text, external URLs, start and end
dates, etc.  Additionally, I'm using the django-tagging app, with the
tagging fields being located in the Node class.  However, because of
the way that the tagging app works, the M2M relationships seem to be
formed with the subclass instance rather than the base class instance.

Let's make this a bit more concrete:

class Node(models.Model):
  author = models.ForeignKey(User)
  created = models.DateTimeField()
  published = models.BooleanField()
  promoted = models.BooleanField()
  section = models.ForeignKey(Section)
  archetype = models.ForeignKey(Archetype)
  title = models.CharField(max_length=255)
  slug = models.SlugField(max_length=25)
  comments_enabled = models.BooleanField()
  tag_list = models.CharField(max_length=255)

  def _get_tags(self):
    return Tag.objects.get_for_object(self)

  def _set_tags(self, tag_list):
    Tag.objects.update_tags(self, tag_list)

  tags = property(_get_tags, _set_tags)

  def get_absolute_url(self):
    return ('sake.node.views.node_detail', (), {
        'sect': self.section.keyword,
        'yr': self.created.year,
        'mo': self.created.strftime('%m'),
        'slg': self.slug})
  get_absolute_url = models.permalink(get_absolute_url)

  def save(self):
    self.archetype = Archetype.objects.get(classname=type
(self).__name__)
    if len(self.slug) == 0:
      self.slug = re.sub('\W', slugrepl, self.title.lower().strip())[:
25]
    super(Node,self).save()
    self.tags = self.tag_list

  def _comment_count(self):
    return self.comment_set.count()

  comment_count = property(_comment_count)

class Article(Node):
  body = models.TextField()

A typical view returns all of the nodes within a given section:

def node_section_all(request, sect):
  try:
    objects = Node.objects.filter(section__keyword=sect,
published=True).order_by('-created')
  except:
    return render_to_response('doesntexist.html', {'identifier':
'node'}, context_instance=RequestContext(request))
  if len(objects) == 0:
    return render_to_response('doesntexist.html', {'identifier':
'node'}, context_instance=RequestContext(request))
  return list_detail.object_list(
      request,
      queryset = Node.objects.filter(section__keyword=sect,
published=True).order_by('-created'),
      template_name = 'node/section_list.html',
      allow_empty = True,
      paginate_by = 10,
  )

The main template calls a custom template tag in order to figure out
what type of node is being rendered and pulls the appropriate template
for that subclass:

{% extends "base.html" %}
{% load tags %}

{% block content %}
    {% for node in object_list %}
    {% render_node node user %}
    {% endfor %}
    <div id="pager">{%comment%}{% pager 9 %}{%endcomment%}</div>
{% endblock %}

Where render_node looks like this:

def render_node(node, user):
  from django.contrib.contenttypes.models import ContentType
  c = ContentType.objects.get(model = node.archetype.classname.lower
())
  template_name = c.app_label + '/' + node.archetype.classname.lower()
+ '_detail.html'
  edit = False
  if user.is_authenticated():
    if user.is_superuser or user.groups.filter
(name=node.section.keyword).count():
      edit = True
  return render_to_string(template_name, {'node': node, 'user': user,
'edit': edit})

register.simple_tag(render_node)

And in the case of the Article subclass, this is the template used to
render each node:

<div class="node">
  <h1>{{ node.title }}</h1>
  <div class="node-body">
    {{ node.article.body|safe }}
  </div>
  <div class="node-footer">
    Posted on {{ node.created|date:"F j, Y" }} by {% if
node.author.get_profile %}<a href="{% url
sake.accounts.views.display_profile username=node.author.username %}">
{{ node.author.get_profile.display_name }}</a>{% else %}
{{ node.author.username }}{% endif %} in <a href="{% url
node.views.node_section_all sect=node.section.keyword %}">
{{ node.section.section }}</a>{% if node.article.tags %} ({% for tag
in node.article.tags %}{% if forloop.last %}<a href="{% url
node.views.node_section_tag sect=node.section.keyword,tag=tag.name|
slugify %}">{{ tag }}</a>{% else %}<a href="{% url
node.views.node_section_tag sect=node.section.keyword,tag=tag.name|
slugify %}">{{ tag }}</a>, {% endif %}{% endfor %}){% endif %}. | <a
href="{% url node.views.node_detail
sect=node.section.keyword,yr=node.created.year,mo=node.created|
date:"m",slg=node.slug %}">Permalink</a> | <a href="{% url
node.views.node_detail
sect=node.section.keyword,yr=node.created.year,mo=node.created|
date:"m",slg=node.slug %}#comment_start">{{ node.comment_count }}
comment{% ifnotequal node.comment_count 1%}s{% endifnotequal %}</a>{%
if edit %} | <a href="{% url article.views.edit_article
sect=node.section.keyword,id=node.id %}">Edit</a> | <a href="{% url
node.views.delete_node sect=node.section.keyword,id=node.id
%}">Delete</a>{% endif %}
  </div>
</div>

As you can see, just about everything displayed needs to reference an
FK in the end template, and then this is iterated many times over the
entire set of nodes.  One easy thing I can do is add select_related()
to the queryset in the view, and that does help some.  But things are
still quite slow, taking 4 to 6 seconds to render the template.

The culprits seem to be the for loop in the template that iterates
over node.article.tags and also the two calls to node.comment_count.
As you can see from the model, node.comment_count is a member function
that returns the value of node.comment_set.count, and it looks like
that isn't being evaluated until the template is rendered.

What it looks like I need is to get my code to do more eager fetching,
but I'm not sure exactly how to go about doing that.  Does anyone have
any thoughts?

Thanks.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-users@googlegroups.com
To unsubscribe from this group, send email to 
django-users+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to