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