Skip to content

Commit

Permalink
Merge pull request hovel#65 from hovel/tracker_mysql_fix
Browse files Browse the repository at this point in the history
fixed issue with get_or_create method for topic/forum read tracker,
  • Loading branch information
zeus committed Mar 4, 2013
2 parents fa2c58f + b2ce128 commit cbaaf2c
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 15 deletions.
47 changes: 46 additions & 1 deletion pybb/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import os.path
import uuid

from django.db import models
from django.db import models, transaction
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.db.utils import IntegrityError
from django.utils.html import strip_tags
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
Expand Down Expand Up @@ -390,6 +391,26 @@ def size_display(self):
return '%.2fMb' % (size / float(1024 * 1024))


class TopicReadTrackerManager(models.Manager):
@transaction.commit_on_success
def get_or_create_tracker(self, user, topic):
"""
Correctly create tracker in mysql db on default REPEATABLE READ transaction mode
It's known problem when standrard get_or_create method return can raise exception
with correct data in mysql database.
See http://stackoverflow.com/questions/2235318/how-do-i-deal-with-this-race-condition-in-django/2235624
"""
is_new = True
try:
obj = TopicReadTracker.objects.create(user=user, topic=topic)
except IntegrityError:
transaction.commit()
obj = TopicReadTracker.objects.get(user=user, topic=topic)
is_new = False
return obj, is_new


class TopicReadTracker(models.Model):
"""
Save per user topic read tracking
Expand All @@ -398,12 +419,34 @@ class TopicReadTracker(models.Model):
topic = models.ForeignKey(Topic, blank=True, null=True)
time_stamp = models.DateTimeField(auto_now=True)

objects = TopicReadTrackerManager()

class Meta(object):
verbose_name = _('Topic read tracker')
verbose_name_plural = _('Topic read trackers')
unique_together = ('user', 'topic')


class ForumReadTrackerManager(models.Manager):
@transaction.commit_on_success
def get_or_create_tracker(self, user, forum):
"""
Correctly create tracker in mysql db on default REPEATABLE READ transaction mode
It's known problem when standrard get_or_create method return can raise exception
with correct data in mysql database.
See http://stackoverflow.com/questions/2235318/how-do-i-deal-with-this-race-condition-in-django/2235624
"""
is_new = True
try:
obj = ForumReadTracker.objects.create(user=user, forum=forum)
except IntegrityError:
transaction.commit()
is_new = False
obj = ForumReadTracker.objects.get(user=user, forum=forum)
return obj, is_new


class ForumReadTracker(models.Model):
"""
Save per user forum read tracking
Expand All @@ -412,6 +455,8 @@ class ForumReadTracker(models.Model):
forum = models.ForeignKey(Forum, blank=True, null=True)
time_stamp = models.DateTimeField(auto_now=True)

objects = ForumReadTrackerManager()

class Meta(object):
verbose_name = _('Forum read tracker')
verbose_name_plural = _('Forum read trackers')
Expand Down
17 changes: 3 additions & 14 deletions pybb/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django.core.urlresolvers import reverse
from django.contrib import messages
from django.db import IntegrityError
from django.db.models import F, Q
from django.http import HttpResponseRedirect, HttpResponse, Http404, HttpResponseBadRequest,\
HttpResponseForbidden
Expand Down Expand Up @@ -191,11 +190,7 @@ def mark_read(self, request, topic):
forum_mark = None
if (forum_mark is None) or (forum_mark.time_stamp < topic.updated):
# Mark topic as readed
try:
topic_mark, new = TopicReadTracker.objects.get_or_create(topic=topic, user=request.user)
except IntegrityError: # duplicate mark
topic_mark = TopicReadTracker.objects.get(topic=topic, user=request.user)
new = False
topic_mark, new = TopicReadTracker.objects.get_or_create_tracker(topic=topic, user=request.user)
if not new:
topic_mark.save()

Expand All @@ -209,10 +204,7 @@ def mark_read(self, request, topic):
user=request.user,
topic__forum=topic.forum
).delete()
try:
forum_mark, new = ForumReadTracker.objects.get_or_create(forum=topic.forum, user=request.user)
except IntegrityError: # duplicate mark
forum_mark = ForumReadTracker.objects.get(forum=topic.forum, user=request.user)
forum_mark, new = ForumReadTracker.objects.get_or_create_tracker(forum=topic.forum, user=request.user)
forum_mark.save()


Expand Down Expand Up @@ -545,10 +537,7 @@ def post_ajax_preview(request):
@login_required
def mark_all_as_read(request):
for forum in perms.filter_forums(request.user, Forum.objects.all()):
try:
forum_mark, new = ForumReadTracker.objects.get_or_create(forum=forum, user=request.user)
except IntegrityError: # duplicate mark
forum_mark = ForumReadTracker.objects.get(forum=forum, user=request.user)
forum_mark, new = ForumReadTracker.objects.get_or_create_tracker(forum=forum, user=request.user)
forum_mark.save()
TopicReadTracker.objects.filter(user=request.user).delete()
msg = _('All forums marked as read')
Expand Down

0 comments on commit cbaaf2c

Please sign in to comment.