view byteflow/discussion/models.py @ 139:24fb49ffc89e
heavy reduced number of queries for post detail view
| author |
Alexander Solovyov <piranha@piranha.org.ua> |
| date |
Wed Oct 31 22:35:04 2007 +0200 (2007-10-31) |
| parents |
d32fb19cb4a4 |
| children |
ebeed4047f19 |
line source
3 from django.db import backend, connection, models
4 from django.contrib.contenttypes.models import ContentType
5 from django.contrib.contenttypes import generic
6 from django.contrib.auth.models import User
7 from django.utils.translation import ugettext_lazy as _
9 from blog.templatetags.nofollow import nofollow
10 from lib.helpers import text_to_html
11 from lib.db import load_related
12 from accounts.models import UserProfile
15 def load_profiles_for_comments(comments):
16 users = [comment.user for comment in comments]
17 load_related(users, UserProfile.objects, field='user', cache_field='profile_cache')
20 class CommentNodeManager(models.Manager):
21 def for_object(self, obj):
23 Create a ``QuerySet`` containing all comments for the given
26 ctype = ContentType.objects.get_for_model(obj)
27 return self.filter(content_type__pk=ctype.id, object_id=obj.id)
29 def tree_for_object(self, obj, filters=None):
31 Get the entire comment tree for an object, with a ``level`
32 attribute added to each comment indicating the level at which
33 it sits in the tree, starting at ``0`` for root comments.
35 comments = self.for_object(obj).select_related().order_by('lft')
37 comments = comments.filter(**filters)
39 for comment in comments:
40 # [:] is used to create a copy of the stack, as the stack will be
41 # modified during iteration.
46 stack_size = len(stack)
47 comment.level = stack_size
48 stack.append(comment.rght)
49 load_profiles_for_comments(comments)
52 def get_counts_in_bulk(self, objects):
54 Get a dictionary mapping object ids to the total number of
55 comments made against each object.
58 SELECT object_id, COUNT(object_id)
60 WHERE content_type_id = %%s
62 GROUP BY object_id""" % (
63 backend.quote_name(self.model._meta.db_table),
64 ','.join(['%s'] * len(objects))
66 ctype = ContentType.objects.get_for_model(objects[0])
67 cursor = connection.cursor()
68 cursor.execute(query, [ctype.id] + [obj.id for obj in objects])
69 results = cursor.fetchall()
70 return dict([(object_id, num_comments) \
71 for object_id, num_comments in results])
73 class CommentNode(models.Model):
75 A comment about any ``Model`` instance, which is also a node in
79 user = models.ForeignKey(User, related_name='comments')
80 pub_date = models.DateTimeField(_(u'Publishing date'), editable=False)
81 body = models.TextField(_(u'Body'))
82 body_html = models.TextField(_(u'Body HTML'), editable=False)
83 reply_to_id = models.PositiveIntegerField(editable=False, null=True, blank=True)
84 approved = models.BooleanField(default=False)
86 # Generic relation to the object being commented on
87 content_type = models.ForeignKey(ContentType)
88 object_id = models.PositiveIntegerField(db_index=True)
89 object = generic.GenericForeignKey('content_type', 'object_id')
92 lft = models.PositiveIntegerField(db_index=True, editable=False)
93 rght = models.PositiveIntegerField(editable=False)
95 objects = CommentNodeManager()
99 self.body = self.body.strip()
100 self.body_html = nofollow(text_to_html(self.body))
102 self.pub_date = datetime.datetime.now()
104 # Get the object being commented on
105 comment_on = self.content_type.get_object_for_this_type(pk=self.object_id)
107 if isinstance(comment_on, CommentNode):
108 # This is a reply to another comment - adopt its content type
109 # and object id so this comment is associated with the correct
111 self.reply_to_id = self.object_id
112 self.content_type = comment_on.content_type
113 self.object_id = comment_on.object_id
115 # We need to update the whole tree to the right of the comment
116 target_rght = comment_on.rght - 1
117 cursor = connection.cursor()
121 WHERE content_type_id = %s
123 AND rght > %s""" % (self.content_type.id, self.object_id, target_rght))
127 WHERE content_type_id = %s
129 AND lft > %s""" % (self.content_type.id, self.object_id, target_rght))
130 self.lft = target_rght + 1
131 self.rght = target_rght + 2
133 # This is a new root comment
134 cursor = connection.cursor()
138 WHERE content_type_id = %s
139 AND object_id = %s""", (self.content_type.id, self.object_id))
140 row = cursor.fetchone()
141 current_max_rght = row[0]
142 if current_max_rght is None:
143 # There are no comments for the content object so far
147 # Put this comment at the top level
148 self.lft = current_max_rght + 1
149 self.rght = current_max_rght + 2
150 super(CommentNode, self).save()
152 def get_absolute_url(self):
153 return '%s#c%s' % (self.object.get_absolute_url(), self.id)
156 db_table = 'comment_nodes'
159 list_display = ('__str__', 'user', 'pub_date', 'content_type', 'object_id', 'reply_to_id', 'approved')
161 def __unicode__(self):
162 return self.body[:50]