diff options
author | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2012-03-05 01:25:52 -0300 |
---|---|---|
committer | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2012-03-05 01:25:52 -0300 |
commit | adbb8d22b4a322fc074b7a4f17805fea6b0bfec6 (patch) | |
tree | 12918bc00da44df4f88571748c5c6257c3b06bb6 | |
parent | 08d0929ae78920e5e92ae7b362a22aac9e5e99b2 (diff) | |
download | askbot-adbb8d22b4a322fc074b7a4f17805fea6b0bfec6.tar.gz askbot-adbb8d22b4a322fc074b7a4f17805fea6b0bfec6.tar.bz2 askbot-adbb8d22b4a322fc074b7a4f17805fea6b0bfec6.zip |
cache invalidation on the question page seems to work
-rw-r--r-- | askbot/const/__init__.py | 4 | ||||
-rw-r--r-- | askbot/models/__init__.py | 16 | ||||
-rw-r--r-- | askbot/models/question.py | 63 | ||||
-rw-r--r-- | askbot/skins/common/media/js/post.js | 16 | ||||
-rw-r--r-- | askbot/skins/common/templates/question/answer_controls.html | 17 | ||||
-rw-r--r-- | askbot/skins/common/templates/question/answer_vote_buttons.html | 22 | ||||
-rw-r--r-- | askbot/skins/common/templates/question/question_controls.html | 10 | ||||
-rw-r--r-- | askbot/skins/default/media/style/style.less | 8 | ||||
-rw-r--r-- | askbot/skins/default/templates/macros.html | 5 | ||||
-rw-r--r-- | askbot/skins/default/templates/question.html | 2 | ||||
-rw-r--r-- | askbot/views/commands.py | 11 | ||||
-rw-r--r-- | askbot/views/readers.py | 1 | ||||
-rw-r--r-- | askbot/views/writers.py | 1 |
13 files changed, 95 insertions, 81 deletions
diff --git a/askbot/const/__init__.py b/askbot/const/__init__.py index ddbff836..66e20dae 100644 --- a/askbot/const/__init__.py +++ b/askbot/const/__init__.py @@ -50,6 +50,10 @@ POST_SORT_METHODS = ( ('votes-asc', _('least voted')), ('relevance-desc', _('relevance')), ) + +ANSWER_SORT_METHODS = (#no translations needed here + 'latest', 'oldest', 'votes' +) #todo: add assertion here that all sort methods are unique #because they are keys to the hash used in implementations #of Q.run_advanced_search diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py index 60ff5fb3..f6c32618 100644 --- a/askbot/models/__init__.py +++ b/askbot/models/__init__.py @@ -991,6 +991,7 @@ def user_post_comment( comment = body_text, added_at = timestamp, ) + parent_post.thread.invalidate_cached_data() award_badges_signal.send(None, event = 'post_comment', actor = self, @@ -1160,6 +1161,7 @@ def user_delete_comment( ): self.assert_can_delete_comment(comment = comment) comment.delete() + comment.thread.invalidate_cached_data() @auto_now_timestamp def user_delete_answer( @@ -1260,6 +1262,7 @@ def user_delete_post( self.delete_question(question = post, timestamp = timestamp) else: raise TypeError('either Comment, Question or Answer expected') + post.thread.invalidate_cached_data() def user_restore_post( self, @@ -1273,6 +1276,7 @@ def user_restore_post( post.deleted_by = None post.deleted_at = None post.save() + post.thread.invalidate_cached_data() if post.post_type == 'answer': post.thread.update_answer_count() else: @@ -1331,10 +1335,13 @@ def user_post_question( def user_edit_comment(self, comment_post=None, body_text = None): """apply edit to a comment, the method does not change the comments timestamp and no signals are sent + todo: see how this can be merged with edit_post + todo: add timestamp """ self.assert_can_edit_comment(comment_post) comment_post.text = body_text comment_post.parse_and_save(author = self) + comment_post.thread.invalidate_cached_data() @auto_now_timestamp @@ -1363,6 +1370,7 @@ def user_edit_question( wiki = wiki, edit_anonymously = edit_anonymously, ) + question.thread.invalidate_cached_data() award_badges_signal.send(None, event = 'edit_question', actor = self, @@ -1389,6 +1397,7 @@ def user_edit_answer( comment = revision_comment, wiki = wiki, ) + answer.thread.invalidate_cached_data() award_badges_signal.send(None, event = 'edit_answer', actor = self, @@ -1957,16 +1966,19 @@ def _process_vote(user, post, timestamp=None, cancel=False, vote_type=None): if vote_type == Vote.VOTE_UP: if cancel: auth.onUpVotedCanceled(vote, post, user, timestamp) - return None else: auth.onUpVoted(vote, post, user, timestamp) elif vote_type == Vote.VOTE_DOWN: if cancel: auth.onDownVotedCanceled(vote, post, user, timestamp) - return None else: auth.onDownVoted(vote, post, user, timestamp) + post.thread.invalidate_cached_data() + + if cancel: + return None + event = VOTES_TO_EVENTS.get((vote_type, post.post_type), None) if event: award_badges_signal.send(None, diff --git a/askbot/models/question.py b/askbot/models/question.py index 0d76a642..6658af9d 100644 --- a/askbot/models/question.py +++ b/askbot/models/question.py @@ -5,9 +5,10 @@ import re from django.conf import settings from django.db import models from django.contrib.auth.models import User -from django.utils.translation import ugettext as _ from django.core import cache # import cache, not from cache import cache, to be able to monkey-patch cache.cache in test cases from django.core.urlresolvers import reverse +from django.utils.hashcompat import md5_constructor +from django.utils.translation import ugettext as _ import askbot import askbot.conf @@ -421,34 +422,42 @@ class Thread(models.Model): | models.Q(deleted_by = user) ) - def get_cached_answer_list(self, sort_method = None): - """get all answer posts as a list for the Thread, and a given - user. This list is cached. + def invalidate_cached_thread_content_fragment(self): + """we do not precache the fragment here, as re-generating + the the fragment takes a lot of data, so we just + invalidate the cached item + + Note: the cache key generation code is copy-pasted + from coffin/template/defaulttags.py no way around + that unfortunately """ - key = self.ANSWER_LIST_KEY_TPL % self.id - answer_list = cache.cache.get(key) - if not answer_list: - answers = self.get_answers() - answers = answers.select_related('thread', 'author', 'last_edited_by') - answers = answers.order_by( - { - "latest":"-added_at", - "oldest":"added_at", - "votes":"-score" - }[sort_method] - ) - answer_list = list(answers) - cache.cache.set(key, answer_list) - return answer_list + args_md5 = md5_constructor(str(self.id)) + key = 'template.cache.%s.%s' % ('thread-content-html', args_md5.hexdigest()) + cache.cache.delete(key) + + def get_post_data_cache_key(self, sort_method = None): + return 'thread-data-%s-%s' % (self.id, sort_method) + + def invalidate_cached_post_data(self): + """needs to be called when anything notable + changes in the post data - on votes, adding, + deleting, editing content""" + #we can call delete_many() here if using Django > 1.2 + for sort_method in const.ANSWER_SORT_METHODS: + cache.cache.delete(self.get_post_data_cache_key(sort_method)) + + def invalidate_cached_data(self): + self.invalidate_cached_post_data() + self.invalidate_cached_thread_content_fragment() def get_cached_post_data(self, sort_method = None): """returns cached post data, as calculated by the method get_post_data()""" - key = 'thread-data-%s-%s' % (self.id, sort_method) + key = self.get_post_data_cache_key(sort_method) post_data = cache.cache.get(key) if not post_data: post_data = self.get_post_data(sort_method) - cache.cache.set(key, post_data) + cache.cache.set(key, post_data, const.LONG_TIME) return post_data def get_post_data(self, sort_method = None): @@ -501,14 +510,17 @@ class Thread(models.Model): except KeyError: pass#comment to deleted answer - don't want it - if self.accepted_answer and self.accepted_answer.deleted == False: + if self.has_accepted_answer() and self.accepted_answer.deleted == False: #Put the accepted answer to front #the second check is for the case when accepted answer is deleted - answers.remove(self.accepted_answer) - answers.insert(0, self.accepted_answer) + accepted_answer = post_map[self.accepted_answer_id] + answers.remove(accepted_answer) + answers.insert(0, accepted_answer) return (question_post, answers, post_to_author) + def has_accepted_answer(self): + return self.accepted_answer_id != None def get_similarity(self, other_thread = None): """return number of tags in the other question @@ -565,6 +577,9 @@ class Thread(models.Model): return similar_threads def get_cached_data(): + """similar thread data will expire + with the default expiration delay + """ key = 'similar-threads-%s' % self.id data = cache.cache.get(key) if data is None: diff --git a/askbot/skins/common/media/js/post.js b/askbot/skins/common/media/js/post.js index dc3fbfd7..0ad59c69 100644 --- a/askbot/skins/common/media/js/post.js +++ b/askbot/skins/common/media/js/post.js @@ -311,7 +311,6 @@ var Vote = function(){ var removeAllOffensiveIdPrefixAnswerFlag = 'answer-offensive-remove-all-flag-'; var offensiveClassFlag = 'offensive-flag'; var questionControlsId = 'question-controls'; - var removeQuestionLinkIdPrefix = 'question-delete-link-'; var removeAnswerLinkIdPrefix = 'answer-delete-link-'; var questionSubscribeUpdates = 'question-subscribe-updates'; var questionSubscribeSidebar= 'question-subscribe-sidebar'; @@ -419,11 +418,6 @@ var Vote = function(){ return $(removeAllOffensiveAnswerFlag); }; - var getremoveQuestionLink = function(){ - var removeQuestionLink = 'div#question-controls a[id^='+ removeQuestionLinkIdPrefix +']'; - return $(removeQuestionLink); - }; - var getquestionSubscribeUpdatesCheckbox = function(){ return $('#' + questionSubscribeUpdates); }; @@ -464,7 +458,7 @@ var Vote = function(){ var bindEvents = function(){ // accept answers - var acceptedButtons = 'div.'+ voteContainerId +' img[id^='+ imgIdPrefixAccept +']'; + var acceptedButtons = 'div.'+ voteContainerId +' div[id^='+ imgIdPrefixAccept +']'; $(acceptedButtons).unbind('click').click(function(event){ Vote.accept($(event.target)); }); @@ -520,10 +514,6 @@ var Vote = function(){ Vote.remove_all_offensive(this, VoteType.removeAllOffensiveAnswer); }); - //getremoveQuestionLink().unbind('click').click(function(event){ - // Vote.remove(this, VoteType.removeQuestion); - //}); - getquestionSubscribeUpdatesCheckbox().unbind('click').click(function(event){ //despeluchar esto if (this.checked){ @@ -578,19 +568,15 @@ var Vote = function(){ showMessage(object, acceptOwnAnswerMessage); } else if(data.status == "1"){ - object.attr("src", mediaUrl("media/images/vote-accepted.png")); $("#"+answerContainerIdPrefix+postId).removeClass("accepted-answer"); $("#"+commentLinkIdPrefix+postId).removeClass("comment-link-accepted"); } else if(data.success == "1"){ - var acceptedButtons = 'div.'+ voteContainerId +' img[id^='+ imgIdPrefixAccept +']'; - $(acceptedButtons).attr("src", mediaUrl("media/images/vote-accepted.png")); var answers = ("div[id^="+answerContainerIdPrefix +"]"); $(answers).removeClass("accepted-answer"); var commentLinks = ("div[id^="+answerContainerIdPrefix +"] div[id^="+ commentLinkIdPrefix +"]"); $(commentLinks).removeClass("comment-link-accepted"); - object.attr("src", mediaUrl("media/images/vote-accepted-on.png")); $("#"+answerContainerIdPrefix+postId).addClass("accepted-answer"); $("#"+commentLinkIdPrefix+postId).addClass("comment-link-accepted"); } diff --git a/askbot/skins/common/templates/question/answer_controls.html b/askbot/skins/common/templates/question/answer_controls.html index 2e8cd4f9..091572af 100644 --- a/askbot/skins/common/templates/question/answer_controls.html +++ b/askbot/skins/common/templates/question/answer_controls.html @@ -12,16 +12,14 @@ <a class="question-delete" >{% if answer.deleted %}{% trans %}undelete{% endtrans %}{% else %}{% trans %}delete{% endtrans %}{% endif %}</a> </span> +{% if answer.offensive_flag_count > 0 %} <span - id="answer-offensive-flag-{{ answer.id }}" + id="answer-offensive-remove-flag-{{ answer.id }}" class="action-link offensive-flag" - title="{% trans %}report as offensive (i.e containing spam, advertising, malicious text, etc.){% endtrans %}" + title="{% trans %}remove offensive flag{% endtrans %}" > - <a class="question-flag">{% trans %}flag offensive{% endtrans %} - <span class="darkred">{% if answer.offensive_flag_count > 0 %}({{ answer.offensive_flag_count }}){% endif %}</span> - </a> + <a class="question-flag">{% trans %}remove flag{% endtrans %}</a> </span> -{% if answer.offensive_flag_count > 0 %} <span id="answer-offensive-flag-{{ answer.id }}" class="action-link offensive-flag" @@ -30,13 +28,6 @@ <a class="question-flag">{% trans %}flag offensive{% endtrans %} ({{ answer.offensive_flag_count }})</a> </a> </span> -<span - id="answer-offensive-flag-remove-{{ answer.id }}" - class="action-link offensive-flag" - title="{% trans %}remove offensive flag{% endtrans %}" -> - <a class="question-flag">{% trans %}remove flag{% endtrans %} ({{ answer.offensive_flag_count }})</a> -</span> {% else %} <span id="answer-offensive-flag-{{ answer.id }}" diff --git a/askbot/skins/common/templates/question/answer_vote_buttons.html b/askbot/skins/common/templates/question/answer_vote_buttons.html index 6ac2e07d..242bf2be 100644 --- a/askbot/skins/common/templates/question/answer_vote_buttons.html +++ b/askbot/skins/common/templates/question/answer_vote_buttons.html @@ -1,20 +1,10 @@ {{ macros.post_vote_buttons(post = answer) }} -<img id="answer-img-accept-{{ answer.id }}" class="answer-img-accept" +<div + id="answer-img-accept-{{ answer.id }}" + class="answer-img-accept" {% if answer.accepted() %} - src="{{'/images/vote-accepted-on.png'|media}}" + title="{% trans %}this answer has been selected as correct{% endtrans %}" {% else %} - src="{{'/images/vote-accepted.png'|media}}" + title="{% trans %}mark this answer as correct (click again to undo){% endtrans %}" {% endif %} - {% if request.user.is_authenticated() and - ( - request.user == question.author or - request.user.is_administrator_or_moderator() - ) - %} - alt="{% trans %}mark this answer as correct (click again to undo){% endtrans %}" - title="{% trans %}mark this answer as correct (click again to undo){% endtrans %}" - {% else %} - alt="{% trans question_author=question.author.username %}{{question_author}} has selected this answer as correct{% endtrans %}" - title="{% trans question_author=question.author.username%}{{question_author}} has selected this answer as correct{% endtrans %}" - {% endif %} - /> +></div> diff --git a/askbot/skins/common/templates/question/question_controls.html b/askbot/skins/common/templates/question/question_controls.html index 4710559d..5eee380a 100644 --- a/askbot/skins/common/templates/question/question_controls.html +++ b/askbot/skins/common/templates/question/question_controls.html @@ -15,17 +15,17 @@ {% endif %} {% if question.offensive_flag_count > 0 %} <span - id="question-offensive-flag-{{ question.id }}" class="offensive-flag" + id="question-offensive-remove-flag-{{ question.id }}" + class="offensive-flag" title="{% trans %}report as offensive (i.e containing spam, advertising, malicious text, etc.){% endtrans %}" > - <a class="question-flag">{% trans %}flag offensive{% endtrans %} {{ question.offensive_flag_count }})</a> + <a class="question-flag">{% trans %}remove flag{% endtrans %}</a> </span> <span - id="question-offensive-flag-remove-{{ question.id }}" - class="offensive-flag" + id="question-offensive-flag-{{ question.id }}" class="offensive-flag" title="{% trans %}report as offensive (i.e containing spam, advertising, malicious text, etc.){% endtrans %}" > - <a class="question-flag">{% trans %}remove flag{% endtrans %} ({{ question.offensive_flag_count }})</a> + <a class="question-flag">{% trans %}flag offensive{% endtrans %} ({{ question.offensive_flag_count }})</a> </span> {% else %} <span diff --git a/askbot/skins/default/media/style/style.less b/askbot/skins/default/media/style/style.less index e8e5a5d8..4f096bd0 100644 --- a/askbot/skins/default/media/style/style.less +++ b/askbot/skins/default/media/style/style.less @@ -2034,9 +2034,17 @@ ul#related-tags li { } } + .answer-img-accept { + background: url(../images/vote-accepted.png); + width: 23px; + height: 23px; + } + + .accepted-answer .answer-img-accept, .answer-img-accept:hover { background: url(../images/vote-accepted-on.png) } + .answer-body a { color:@link; } diff --git a/askbot/skins/default/templates/macros.html b/askbot/skins/default/templates/macros.html index b5c052cc..e0de6bb6 100644 --- a/askbot/skins/default/templates/macros.html +++ b/askbot/skins/default/templates/macros.html @@ -303,9 +303,8 @@ for the purposes of the AJAX comment editor #} {{comment.html}} <a class="author" href="{{comment.author.get_profile_url()}}">{{comment.author.username}}</a> <span class="age"> ({{comment.added_at|diff_date}})</span> - <a - id="post-{{comment.id}}-edit" - class="edit">{% trans %}edit{% endtrans %}</a> + <a id="post-{{comment.id}}-edit" + class="edit">{% trans %}edit{% endtrans %}</a> </div> </div> <script type="text/javascript"> diff --git a/askbot/skins/default/templates/question.html b/askbot/skins/default/templates/question.html index e241b9b2..bc0dbdeb 100644 --- a/askbot/skins/default/templates/question.html +++ b/askbot/skins/default/templates/question.html @@ -123,7 +123,7 @@ {% endblock %} {% block content %} {% if is_cacheable %} - {% cache 1000 "thread" thread.id %} + {% cache long_time "thread-content-html" thread.id %} {% include "question/content.html" %} {% endcache %} {% else %} diff --git a/askbot/views/commands.py b/askbot/views/commands.py index a6de376b..5d86d1a1 100644 --- a/askbot/views/commands.py +++ b/askbot/views/commands.py @@ -288,10 +288,10 @@ def vote(request, id): elif vote_type in ['7.6', '8.6']: #flag question or answer if vote_type == '7.6': - post = get_object_or_404(models.Question, id=id) + post = get_object_or_404(models.Post, id=id) if vote_type == '8.6': id = request.POST.get('postId') - post = get_object_or_404(models.Answer, id=id) + post = get_object_or_404(models.Post, id=id) request.user.flag_post(post, cancel_all = True) @@ -360,6 +360,13 @@ def vote(request, id): response_data['success'] = 0 response_data['message'] = u'Request mode is not supported. Please try again.' + if vote_type not in (1, 2, 4, 5, 6, 11, 12): + #favorite or subscribe/unsubscribe + #upvote or downvote question or answer - those + #are handled within user.upvote and user.downvote + post = models.Post.objects.get(id = id) + post.thread.invalidate_cached_data() + data = simplejson.dumps(response_data) except Exception, e: diff --git a/askbot/views/readers.py b/askbot/views/readers.py index 00d2fd5a..b6d4fb72 100644 --- a/askbot/views/readers.py +++ b/askbot/views/readers.py @@ -526,6 +526,7 @@ def question(request, id):#refactor - long subroutine. display question body, an data = { 'is_cacheable': is_cacheable, + 'long_time': const.LONG_TIME,#"forever" caching 'page_class': 'question-page', 'active_tab': 'questions', 'question' : question_post, diff --git a/askbot/views/writers.py b/askbot/views/writers.py index b7e0984e..ab7f581e 100644 --- a/askbot/views/writers.py +++ b/askbot/views/writers.py @@ -641,6 +641,7 @@ def delete_comment(request): #attn: recalc denormalized field parent.comment_count = parent.comment_count - 1 parent.save() + parent.thread.invalidate_cached_data() return __generate_comments_json(parent, request.user) |