##// END OF EJS Templates
Optimized loading post images in post template
neko259 -
r694:597b8a7d 1.8-dev
parent child Browse files
Show More
@@ -1,164 +1,163 b''
1 import logging
1 import logging
2 from django.db.models import Count
2 from django.db.models import Count
3 from django.utils import timezone
3 from django.utils import timezone
4 from django.core.cache import cache
4 from django.core.cache import cache
5 from django.db import models
5 from django.db import models
6 from neboard import settings
6 from neboard import settings
7
7
8 __author__ = 'neko259'
8 __author__ = 'neko259'
9
9
10
10
11 logger = logging.getLogger(__name__)
11 logger = logging.getLogger(__name__)
12
12
13
13
14 CACHE_KEY_OPENING_POST = 'opening_post_id'
14 CACHE_KEY_OPENING_POST = 'opening_post_id'
15
15
16
16
17 class Thread(models.Model):
17 class Thread(models.Model):
18
18
19 class Meta:
19 class Meta:
20 app_label = 'boards'
20 app_label = 'boards'
21
21
22 tags = models.ManyToManyField('Tag')
22 tags = models.ManyToManyField('Tag')
23 bump_time = models.DateTimeField()
23 bump_time = models.DateTimeField()
24 last_edit_time = models.DateTimeField()
24 last_edit_time = models.DateTimeField()
25 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
25 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
26 blank=True, related_name='tre+')
26 blank=True, related_name='tre+')
27 archived = models.BooleanField(default=False)
27 archived = models.BooleanField(default=False)
28
28
29 def get_tags(self):
29 def get_tags(self):
30 """
30 """
31 Gets a sorted tag list.
31 Gets a sorted tag list.
32 """
32 """
33
33
34 return self.tags.order_by('name')
34 return self.tags.order_by('name')
35
35
36 def bump(self):
36 def bump(self):
37 """
37 """
38 Bumps (moves to up) thread if possible.
38 Bumps (moves to up) thread if possible.
39 """
39 """
40
40
41 if self.can_bump():
41 if self.can_bump():
42 self.bump_time = timezone.now()
42 self.bump_time = timezone.now()
43
43
44 logger.info('Bumped thread %d' % self.id)
44 logger.info('Bumped thread %d' % self.id)
45
45
46 def get_reply_count(self):
46 def get_reply_count(self):
47 return self.replies.count()
47 return self.replies.count()
48
48
49 def get_images_count(self):
49 def get_images_count(self):
50 # TODO Use sum
50 # TODO Use sum
51 total_count = 0
51 total_count = 0
52 for post_with_image in self.replies.annotate(images_count=Count(
52 for post_with_image in self.replies.annotate(images_count=Count(
53 'images')):
53 'images')):
54 total_count += post_with_image.images_count
54 total_count += post_with_image.images_count
55 return total_count
55 return total_count
56
56
57 def can_bump(self):
57 def can_bump(self):
58 """
58 """
59 Checks if the thread can be bumped by replying to it.
59 Checks if the thread can be bumped by replying to it.
60 """
60 """
61
61
62 if self.archived:
62 if self.archived:
63 return False
63 return False
64
64
65 post_count = self.get_reply_count()
65 post_count = self.get_reply_count()
66
66
67 return post_count < settings.MAX_POSTS_PER_THREAD
67 return post_count < settings.MAX_POSTS_PER_THREAD
68
68
69 def delete_with_posts(self):
69 def delete_with_posts(self):
70 """
70 """
71 Completely deletes thread and all its posts
71 Completely deletes thread and all its posts
72 """
72 """
73
73
74 if self.replies.exists():
74 if self.replies.exists():
75 self.replies.all().delete()
75 self.replies.all().delete()
76
76
77 self.delete()
77 self.delete()
78
78
79 def get_last_replies(self):
79 def get_last_replies(self):
80 """
80 """
81 Gets several last replies, not including opening post
81 Gets several last replies, not including opening post
82 """
82 """
83
83
84 if settings.LAST_REPLIES_COUNT > 0:
84 if settings.LAST_REPLIES_COUNT > 0:
85 reply_count = self.get_reply_count()
85 reply_count = self.get_reply_count()
86
86
87 if reply_count > 0:
87 if reply_count > 0:
88 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
88 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
89 reply_count - 1)
89 reply_count - 1)
90 replies = self.replies.order_by('pub_time').defer(
90 replies = self.get_replies()
91 'poster_user_agent', 'text_markup_type')
92 last_replies = replies[reply_count - reply_count_to_show:]
91 last_replies = replies[reply_count - reply_count_to_show:]
93
92
94 return last_replies
93 return last_replies
95
94
96 def get_skipped_replies_count(self):
95 def get_skipped_replies_count(self):
97 """
96 """
98 Gets number of posts between opening post and last replies.
97 Gets number of posts between opening post and last replies.
99 """
98 """
100 reply_count = self.get_reply_count()
99 reply_count = self.get_reply_count()
101 last_replies_count = min(settings.LAST_REPLIES_COUNT,
100 last_replies_count = min(settings.LAST_REPLIES_COUNT,
102 reply_count - 1)
101 reply_count - 1)
103 return reply_count - last_replies_count - 1
102 return reply_count - last_replies_count - 1
104
103
105 def get_replies(self, view_fields_only=False):
104 def get_replies(self, view_fields_only=False):
106 """
105 """
107 Gets sorted thread posts
106 Gets sorted thread posts
108 """
107 """
109
108
110 query = self.replies.order_by('pub_time')
109 query = self.replies.order_by('pub_time').prefetch_related('images')
111 if view_fields_only:
110 if view_fields_only:
112 query = query.defer('poster_user_agent', 'text_markup_type')
111 query = query.defer('poster_user_agent', 'text_markup_type')
113 return query.all()
112 return query.all()
114
113
115 def get_replies_with_images(self, view_fields_only=False):
114 def get_replies_with_images(self, view_fields_only=False):
116 return self.get_replies(view_fields_only).annotate(images_count=Count(
115 return self.get_replies(view_fields_only).annotate(images_count=Count(
117 'images')).filter(images_count__gt=0)
116 'images')).filter(images_count__gt=0)
118
117
119 def add_tag(self, tag):
118 def add_tag(self, tag):
120 """
119 """
121 Connects thread to a tag and tag to a thread
120 Connects thread to a tag and tag to a thread
122 """
121 """
123
122
124 self.tags.add(tag)
123 self.tags.add(tag)
125 tag.threads.add(self)
124 tag.threads.add(self)
126
125
127 def remove_tag(self, tag):
126 def remove_tag(self, tag):
128 self.tags.remove(tag)
127 self.tags.remove(tag)
129 tag.threads.remove(self)
128 tag.threads.remove(self)
130
129
131 def get_opening_post(self, only_id=False):
130 def get_opening_post(self, only_id=False):
132 """
131 """
133 Gets the first post of the thread
132 Gets the first post of the thread
134 """
133 """
135
134
136 query = self.replies.order_by('pub_time')
135 query = self.replies.order_by('pub_time')
137 if only_id:
136 if only_id:
138 query = query.only('id')
137 query = query.only('id')
139 opening_post = query.first()
138 opening_post = query.first()
140
139
141 return opening_post
140 return opening_post
142
141
143 def get_opening_post_id(self):
142 def get_opening_post_id(self):
144 """
143 """
145 Gets ID of the first thread post.
144 Gets ID of the first thread post.
146 """
145 """
147
146
148 cache_key = CACHE_KEY_OPENING_POST + str(self.id)
147 cache_key = CACHE_KEY_OPENING_POST + str(self.id)
149 opening_post_id = cache.get(cache_key)
148 opening_post_id = cache.get(cache_key)
150 if not opening_post_id:
149 if not opening_post_id:
151 opening_post_id = self.get_opening_post(only_id=True).id
150 opening_post_id = self.get_opening_post(only_id=True).id
152 cache.set(cache_key, opening_post_id)
151 cache.set(cache_key, opening_post_id)
153
152
154 return opening_post_id
153 return opening_post_id
155
154
156 def __unicode__(self):
155 def __unicode__(self):
157 return str(self.id)
156 return str(self.id)
158
157
159 def get_pub_time(self):
158 def get_pub_time(self):
160 """
159 """
161 Gets opening post's pub time because thread does not have its own one.
160 Gets opening post's pub time because thread does not have its own one.
162 """
161 """
163
162
164 return self.get_opening_post().pub_time No newline at end of file
163 return self.get_opening_post().pub_time
@@ -1,98 +1,98 b''
1 {% load i18n %}
1 {% load i18n %}
2 {% load board %}
2 {% load board %}
3 {% load cache %}
3 {% load cache %}
4
4
5 {% get_current_language as LANGUAGE_CODE %}
5 {% get_current_language as LANGUAGE_CODE %}
6
6
7 {% spaceless %}
7 {% spaceless %}
8 {% cache 600 post post.id post.last_edit_time thread.archived bumpable truncated moderator LANGUAGE_CODE need_open_link %}
8 {% cache 600 post post.id post.last_edit_time thread.archived bumpable truncated moderator LANGUAGE_CODE need_open_link %}
9 {% if thread.archived %}
9 {% if thread.archived %}
10 <div class="post archive_post" id="{{ post.id }}">
10 <div class="post archive_post" id="{{ post.id }}">
11 {% elif bumpable %}
11 {% elif bumpable %}
12 <div class="post" id="{{ post.id }}">
12 <div class="post" id="{{ post.id }}">
13 {% else %}
13 {% else %}
14 <div class="post dead_post" id="{{ post.id }}">
14 <div class="post dead_post" id="{{ post.id }}">
15 {% endif %}
15 {% endif %}
16
16
17 <div class="post-info">
17 <div class="post-info">
18 <a class="post_id" href="{% post_object_url post thread=thread %}"
18 <a class="post_id" href="{% post_object_url post thread=thread %}"
19 {% if not truncated and not thread.archived %}
19 {% if not truncated and not thread.archived %}
20 onclick="javascript:addQuickReply('{{ post.id }}'); return false;"
20 onclick="javascript:addQuickReply('{{ post.id }}'); return false;"
21 title="{% trans 'Quote' %}"
21 title="{% trans 'Quote' %}"
22 {% endif %}
22 {% endif %}
23 >({{ post.id }}) </a>
23 >({{ post.id }}) </a>
24 <span class="title">{{ post.title }} </span>
24 <span class="title">{{ post.title }} </span>
25 <span class="pub_time">{{ post.pub_time }}</span>
25 <span class="pub_time">{{ post.pub_time }}</span>
26 {% if thread.archived %}
26 {% if thread.archived %}
27 β€” {{ thread.bump_time }}
27 β€” {{ thread.bump_time }}
28 {% endif %}
28 {% endif %}
29 {% if is_opening and need_open_link %}
29 {% if is_opening and need_open_link %}
30 {% if thread.archived %}
30 {% if thread.archived %}
31 [<a class="link" href="{% url 'thread' post.id %}">{% trans "Open" %}</a>]
31 [<a class="link" href="{% url 'thread' post.id %}">{% trans "Open" %}</a>]
32 {% else %}
32 {% else %}
33 [<a class="link" href="{% url 'thread' post.id %}#form">{% trans "Reply" %}</a>]
33 [<a class="link" href="{% url 'thread' post.id %}#form">{% trans "Reply" %}</a>]
34 {% endif %}
34 {% endif %}
35 {% endif %}
35 {% endif %}
36
36
37 {% if moderator %}
37 {% if moderator %}
38 <span class="moderator_info">
38 <span class="moderator_info">
39 [<a href="{% url 'post_admin' post_id=post.id %}"
39 [<a href="{% url 'post_admin' post_id=post.id %}"
40 >{% trans 'Edit' %}</a>]
40 >{% trans 'Edit' %}</a>]
41 [<a href="{% url 'delete' post_id=post.id %}"
41 [<a href="{% url 'delete' post_id=post.id %}"
42 >{% trans 'Delete' %}</a>]
42 >{% trans 'Delete' %}</a>]
43 ({{ post.poster_ip }})
43 ({{ post.poster_ip }})
44 [<a href="{% url 'ban' post_id=post.id %}?next={{ request.path }}"
44 [<a href="{% url 'ban' post_id=post.id %}?next={{ request.path }}"
45 >{% trans 'Ban IP' %}</a>]
45 >{% trans 'Ban IP' %}</a>]
46 </span>
46 </span>
47 {% endif %}
47 {% endif %}
48 </div>
48 </div>
49 {% if post.images.exists %}
49 {% if post.images.exists %}
50 {% with post.get_first_image as image %}
50 {% with post.images.all.0 as image %}
51 <div class="image">
51 <div class="image">
52 <a
52 <a
53 class="thumb"
53 class="thumb"
54 href="{{ image.image.url }}"><img
54 href="{{ image.image.url }}"><img
55 src="{{ image.image.url_200x150 }}"
55 src="{{ image.image.url_200x150 }}"
56 alt="{{ post.id }}"
56 alt="{{ post.id }}"
57 width="{{ image.pre_width }}"
57 width="{{ image.pre_width }}"
58 height="{{ image.pre_height }}"
58 height="{{ image.pre_height }}"
59 data-width="{{ image.width }}"
59 data-width="{{ image.width }}"
60 data-height="{{ image.height }}"/>
60 data-height="{{ image.height }}"/>
61 </a>
61 </a>
62 </div>
62 </div>
63 {% endwith %}
63 {% endwith %}
64 {% endif %}
64 {% endif %}
65 <div class="message">
65 <div class="message">
66 {% autoescape off %}
66 {% autoescape off %}
67 {% if truncated %}
67 {% if truncated %}
68 {{ post.text.rendered|truncatewords_html:50 }}
68 {{ post.text.rendered|truncatewords_html:50 }}
69 {% else %}
69 {% else %}
70 {{ post.text.rendered }}
70 {{ post.text.rendered }}
71 {% endif %}
71 {% endif %}
72 {% endautoescape %}
72 {% endautoescape %}
73 {% if post.is_referenced %}
73 {% if post.is_referenced %}
74 <div class="refmap">
74 <div class="refmap">
75 {% autoescape off %}
75 {% autoescape off %}
76 {% trans "Replies" %}: {{ post.refmap }}
76 {% trans "Replies" %}: {{ post.refmap }}
77 {% endautoescape %}
77 {% endautoescape %}
78 </div>
78 </div>
79 {% endif %}
79 {% endif %}
80 </div>
80 </div>
81 {% endcache %}
81 {% endcache %}
82 {% if is_opening %}
82 {% if is_opening %}
83 {% cache 600 post_thread thread.id thread.last_edit_time LANGUAGE_CODE need_open_link %}
83 {% cache 600 post_thread thread.id thread.last_edit_time LANGUAGE_CODE need_open_link %}
84 <div class="metadata">
84 <div class="metadata">
85 {% if is_opening and need_open_link %}
85 {% if is_opening and need_open_link %}
86 {{ thread.get_images_count }} {% trans 'images' %}.
86 {{ thread.get_images_count }} {% trans 'images' %}.
87 {% endif %}
87 {% endif %}
88 <span class="tags">
88 <span class="tags">
89 {% for tag in thread.get_tags %}
89 {% for tag in thread.get_tags %}
90 <a class="tag" href="{% url 'tag' tag.name %}">
90 <a class="tag" href="{% url 'tag' tag.name %}">
91 #{{ tag.name }}</a>{% if not forloop.last %},{% endif %}
91 #{{ tag.name }}</a>{% if not forloop.last %},{% endif %}
92 {% endfor %}
92 {% endfor %}
93 </span>
93 </span>
94 </div>
94 </div>
95 {% endcache %}
95 {% endcache %}
96 {% endif %}
96 {% endif %}
97 </div>
97 </div>
98 {% endspaceless %}
98 {% endspaceless %}
General Comments 0
You need to be logged in to leave comments. Login now