##// END OF EJS Templates
Use more efficient way to get last replies
neko259 -
r986:68e7bfc6 default
parent child Browse files
Show More
@@ -1,185 +1,185 b''
1 import logging
1 import logging
2
2
3 from django.db.models import Count, Sum
3 from django.db.models import Count, Sum
4 from django.utils import timezone
4 from django.utils import timezone
5 from django.db import models
5 from django.db import models
6
6
7 from boards import settings
7 from boards import settings
8 from boards.utils import cached_result
8 from boards.utils import cached_result
9 from boards.models.post import Post
9 from boards.models.post import Post
10
10
11
11
12 __author__ = 'neko259'
12 __author__ = 'neko259'
13
13
14
14
15 logger = logging.getLogger(__name__)
15 logger = logging.getLogger(__name__)
16
16
17
17
18 class ThreadManager(models.Manager):
18 class ThreadManager(models.Manager):
19 def process_oldest_threads(self):
19 def process_oldest_threads(self):
20 """
20 """
21 Preserves maximum thread count. If there are too many threads,
21 Preserves maximum thread count. If there are too many threads,
22 archive or delete the old ones.
22 archive or delete the old ones.
23 """
23 """
24
24
25 threads = Thread.objects.filter(archived=False).order_by('-bump_time')
25 threads = Thread.objects.filter(archived=False).order_by('-bump_time')
26 thread_count = threads.count()
26 thread_count = threads.count()
27
27
28 if thread_count > settings.MAX_THREAD_COUNT:
28 if thread_count > settings.MAX_THREAD_COUNT:
29 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
29 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
30 old_threads = threads[thread_count - num_threads_to_delete:]
30 old_threads = threads[thread_count - num_threads_to_delete:]
31
31
32 for thread in old_threads:
32 for thread in old_threads:
33 if settings.ARCHIVE_THREADS:
33 if settings.ARCHIVE_THREADS:
34 self._archive_thread(thread)
34 self._archive_thread(thread)
35 else:
35 else:
36 thread.delete()
36 thread.delete()
37
37
38 logger.info('Processed %d old threads' % num_threads_to_delete)
38 logger.info('Processed %d old threads' % num_threads_to_delete)
39
39
40 def _archive_thread(self, thread):
40 def _archive_thread(self, thread):
41 thread.archived = True
41 thread.archived = True
42 thread.bumpable = False
42 thread.bumpable = False
43 thread.last_edit_time = timezone.now()
43 thread.last_edit_time = timezone.now()
44 thread.save(update_fields=['archived', 'last_edit_time', 'bumpable'])
44 thread.save(update_fields=['archived', 'last_edit_time', 'bumpable'])
45
45
46
46
47 class Thread(models.Model):
47 class Thread(models.Model):
48 objects = ThreadManager()
48 objects = ThreadManager()
49
49
50 class Meta:
50 class Meta:
51 app_label = 'boards'
51 app_label = 'boards'
52
52
53 tags = models.ManyToManyField('Tag')
53 tags = models.ManyToManyField('Tag')
54 bump_time = models.DateTimeField(db_index=True)
54 bump_time = models.DateTimeField(db_index=True)
55 last_edit_time = models.DateTimeField()
55 last_edit_time = models.DateTimeField()
56 archived = models.BooleanField(default=False)
56 archived = models.BooleanField(default=False)
57 bumpable = models.BooleanField(default=True)
57 bumpable = models.BooleanField(default=True)
58
58
59 def get_tags(self):
59 def get_tags(self):
60 """
60 """
61 Gets a sorted tag list.
61 Gets a sorted tag list.
62 """
62 """
63
63
64 return self.tags.order_by('name')
64 return self.tags.order_by('name')
65
65
66 def bump(self):
66 def bump(self):
67 """
67 """
68 Bumps (moves to up) thread if possible.
68 Bumps (moves to up) thread if possible.
69 """
69 """
70
70
71 if self.can_bump():
71 if self.can_bump():
72 self.bump_time = timezone.now()
72 self.bump_time = timezone.now()
73
73
74 if self.get_reply_count() >= settings.MAX_POSTS_PER_THREAD:
74 if self.get_reply_count() >= settings.MAX_POSTS_PER_THREAD:
75 self.bumpable = False
75 self.bumpable = False
76
76
77 logger.info('Bumped thread %d' % self.id)
77 logger.info('Bumped thread %d' % self.id)
78
78
79 def get_reply_count(self):
79 def get_reply_count(self):
80 return self.get_replies().count()
80 return self.get_replies().count()
81
81
82 def get_images_count(self):
82 def get_images_count(self):
83 return self.get_replies().annotate(images_count=Count(
83 return self.get_replies().annotate(images_count=Count(
84 'images')).aggregate(Sum('images_count'))['images_count__sum']
84 'images')).aggregate(Sum('images_count'))['images_count__sum']
85
85
86 def can_bump(self):
86 def can_bump(self):
87 """
87 """
88 Checks if the thread can be bumped by replying to it.
88 Checks if the thread can be bumped by replying to it.
89 """
89 """
90
90
91 return self.bumpable
91 return self.bumpable
92
92
93 def get_last_replies(self):
93 def get_last_replies(self):
94 """
94 """
95 Gets several last replies, not including opening post
95 Gets several last replies, not including opening post
96 """
96 """
97
97
98 if settings.LAST_REPLIES_COUNT > 0:
98 if settings.LAST_REPLIES_COUNT > 0:
99 reply_count = self.get_reply_count()
99 reply_count = self.get_reply_count()
100
100
101 if reply_count > 0:
101 if reply_count > 0:
102 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
102 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
103 reply_count - 1)
103 reply_count - 1)
104 replies = self.get_replies()
104 replies = self.get_replies()
105 last_replies = replies[reply_count - reply_count_to_show:]
105 last_replies = replies.reverse()[:reply_count_to_show]
106
106
107 return last_replies
107 return last_replies
108
108
109 def get_skipped_replies_count(self):
109 def get_skipped_replies_count(self):
110 """
110 """
111 Gets number of posts between opening post and last replies.
111 Gets number of posts between opening post and last replies.
112 """
112 """
113 reply_count = self.get_reply_count()
113 reply_count = self.get_reply_count()
114 last_replies_count = min(settings.LAST_REPLIES_COUNT,
114 last_replies_count = min(settings.LAST_REPLIES_COUNT,
115 reply_count - 1)
115 reply_count - 1)
116 return reply_count - last_replies_count - 1
116 return reply_count - last_replies_count - 1
117
117
118 def get_replies(self, view_fields_only=False):
118 def get_replies(self, view_fields_only=False):
119 """
119 """
120 Gets sorted thread posts
120 Gets sorted thread posts
121 """
121 """
122
122
123 query = Post.objects.filter(threads__in=[self])
123 query = Post.objects.filter(threads__in=[self])
124 query = query.order_by('pub_time').prefetch_related('images', 'thread', 'threads')
124 query = query.order_by('pub_time').prefetch_related('images', 'thread', 'threads')
125 if view_fields_only:
125 if view_fields_only:
126 query = query.defer('poster_user_agent')
126 query = query.defer('poster_user_agent')
127 return query.all()
127 return query.all()
128
128
129 def get_replies_with_images(self, view_fields_only=False):
129 def get_replies_with_images(self, view_fields_only=False):
130 """
130 """
131 Gets replies that have at least one image attached
131 Gets replies that have at least one image attached
132 """
132 """
133
133
134 return self.get_replies(view_fields_only).annotate(images_count=Count(
134 return self.get_replies(view_fields_only).annotate(images_count=Count(
135 'images')).filter(images_count__gt=0)
135 'images')).filter(images_count__gt=0)
136
136
137 def add_tag(self, tag):
137 def add_tag(self, tag):
138 """
138 """
139 Connects thread to a tag and tag to a thread
139 Connects thread to a tag and tag to a thread
140 """
140 """
141
141
142 self.tags.add(tag)
142 self.tags.add(tag)
143
143
144 def get_opening_post(self, only_id=False):
144 def get_opening_post(self, only_id=False):
145 """
145 """
146 Gets the first post of the thread
146 Gets the first post of the thread
147 """
147 """
148
148
149 query = self.get_replies().order_by('pub_time')
149 query = self.get_replies().order_by('pub_time')
150 if only_id:
150 if only_id:
151 query = query.only('id')
151 query = query.only('id')
152 opening_post = query.first()
152 opening_post = query.first()
153
153
154 return opening_post
154 return opening_post
155
155
156 @cached_result
156 @cached_result
157 def get_opening_post_id(self):
157 def get_opening_post_id(self):
158 """
158 """
159 Gets ID of the first thread post.
159 Gets ID of the first thread post.
160 """
160 """
161
161
162 return self.get_opening_post(only_id=True).id
162 return self.get_opening_post(only_id=True).id
163
163
164 def __unicode__(self):
164 def __unicode__(self):
165 return str(self.id)
165 return str(self.id)
166
166
167 def get_pub_time(self):
167 def get_pub_time(self):
168 """
168 """
169 Gets opening post's pub time because thread does not have its own one.
169 Gets opening post's pub time because thread does not have its own one.
170 """
170 """
171
171
172 return self.get_opening_post().pub_time
172 return self.get_opening_post().pub_time
173
173
174 def delete(self, using=None):
174 def delete(self, using=None):
175 """
175 """
176 Deletes thread with all replies.
176 Deletes thread with all replies.
177 """
177 """
178
178
179 for reply in self.get_replies().all():
179 for reply in self.get_replies().all():
180 reply.delete()
180 reply.delete()
181
181
182 super(Thread, self).delete(using)
182 super(Thread, self).delete(using)
183
183
184 def __str__(self):
184 def __str__(self):
185 return 'T#{}/{}'.format(self.id, self.get_opening_post_id())
185 return 'T#{}/{}'.format(self.id, self.get_opening_post_id())
General Comments 0
You need to be logged in to leave comments. Login now