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