Commit 08312fc2 by Luke Plant

Fix for issue #225 (exception when running with ATOMIC_REQUESTS), and the same…

Fix for issue #225 (exception when running with ATOMIC_REQUESTS), and the same applied to deleting subtrees

All explicit transaction handling has been moved to the model layer, and
uses transaction.commit_on_success or transaction.atomic, depending on the
Django version.
parent 26ce59d1
from django.db import transaction
# Django 1.6 transaction API, required for 1.8+
def nop_decorator(func):
return func
# Where these are used in code, both old and new methods for transactions appear
# to be used, but only one will actually do anything. When only Django 1.8+
# is supported, transaction_commit_on_success can be deleted.
try:
atomic = transaction.atomic # Does it exist?
transaction_commit_on_success = nop_decorator
except AttributeError:
atomic = nop_decorator
transaction_commit_on_success = transaction.commit_on_success
...@@ -6,13 +6,7 @@ from django.contrib.contenttypes.models import ContentType ...@@ -6,13 +6,7 @@ from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import models, transaction from django.db import models
#Django 1.6 transaction API, required for 1.8+
try:
notrans=transaction.non_atomic_requests
except:
notrans=transaction.commit_manually
from django.db.models.signals import post_save, pre_delete from django.db.models.signals import post_save, pre_delete
from django.utils.translation import ugettext_lazy as _, ugettext from django.utils.translation import ugettext_lazy as _, ugettext
...@@ -21,6 +15,7 @@ from mptt.fields import TreeForeignKey ...@@ -21,6 +15,7 @@ from mptt.fields import TreeForeignKey
from mptt.models import MPTTModel from mptt.models import MPTTModel
from wiki import managers from wiki import managers
from wiki.compat import atomic, transaction_commit_on_success
from wiki.conf import settings from wiki.conf import settings
from wiki.core.exceptions import NoRootURL, MultipleRootURLs from wiki.core.exceptions import NoRootURL, MultipleRootURLs
from wiki.models.article import ArticleRevision, ArticleForObject, Article from wiki.models.article import ArticleRevision, ArticleForObject, Article
...@@ -115,24 +110,16 @@ class URLPath(MPTTModel): ...@@ -115,24 +110,16 @@ class URLPath(MPTTModel):
return ancestor return ancestor
return None return None
@notrans @atomic
@transaction_commit_on_success
def delete_subtree(self): def delete_subtree(self):
""" """
NB! This deletes this urlpath, its children, and ALL of the related NB! This deletes this urlpath, its children, and ALL of the related
articles. This is a purged delete and CANNOT be undone. articles. This is a purged delete and CANNOT be undone.
""" """
try:
for descendant in self.get_descendants(include_self=True).order_by("-level"): for descendant in self.get_descendants(include_self=True).order_by("-level"):
print "deleting " , descendant
descendant.article.delete() descendant.article.delete()
transaction.commit()
except:
transaction.rollback()
log.exception("Exception deleting article subtree.")
@classmethod @classmethod
def root(cls): def root(cls):
site = Site.objects.get_current() site = Site.objects.get_current()
...@@ -233,6 +220,8 @@ class URLPath(MPTTModel): ...@@ -233,6 +220,8 @@ class URLPath(MPTTModel):
return root return root
@classmethod @classmethod
@atomic
@transaction_commit_on_success
def create_article(cls, parent, slug, site=None, title="Root", article_kwargs={}, **kwargs): def create_article(cls, parent, slug, site=None, title="Root", article_kwargs={}, **kwargs):
"""Utility function: """Utility function:
Create a new urlpath with an article and a new revision for the article""" Create a new urlpath with an article and a new revision for the article"""
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import difflib import difflib
import logging
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
...@@ -19,18 +20,13 @@ from wiki.core.plugins import registry as plugin_registry ...@@ -19,18 +20,13 @@ from wiki.core.plugins import registry as plugin_registry
from wiki.core.diff import simple_merge from wiki.core.diff import simple_merge
from wiki.decorators import get_article, json_view from wiki.decorators import get_article, json_view
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import transaction
#Django 1.6 transaction API, required for 1.8+
try:
notrans=transaction.non_atomic_requests
except:
notrans=transaction.commit_manually
from wiki.core.exceptions import NoRootURL from wiki.core.exceptions import NoRootURL
from wiki.core import permissions from wiki.core import permissions
from django.http import Http404 from django.http import Http404
log = logging.getLogger(__name__)
class ArticleView(ArticleMixin, TemplateView): class ArticleView(ArticleMixin, TemplateView):
...@@ -67,7 +63,6 @@ class Create(FormView, ArticleMixin): ...@@ -67,7 +63,6 @@ class Create(FormView, ArticleMixin):
form.fields['slug'].widget = forms.TextInputPrepend(prepend='/'+self.urlpath.path) form.fields['slug'].widget = forms.TextInputPrepend(prepend='/'+self.urlpath.path)
return form return form
@notrans
def form_valid(self, form): def form_valid(self, form):
user=None user=None
ip_address = None ip_address = None
...@@ -77,6 +72,7 @@ class Create(FormView, ArticleMixin): ...@@ -77,6 +72,7 @@ class Create(FormView, ArticleMixin):
ip_address = self.request.META.get('REMOTE_ADDR', None) ip_address = self.request.META.get('REMOTE_ADDR', None)
elif settings.LOG_IPS_ANONYMOUS: elif settings.LOG_IPS_ANONYMOUS:
ip_address = self.request.META.get('REMOTE_ADDR', None) ip_address = self.request.META.get('REMOTE_ADDR', None)
try: try:
self.newpath = models.URLPath.create_article( self.newpath = models.URLPath.create_article(
self.urlpath, self.urlpath,
...@@ -94,20 +90,15 @@ class Create(FormView, ArticleMixin): ...@@ -94,20 +90,15 @@ class Create(FormView, ArticleMixin):
'other_write': self.article.other_write, 'other_write': self.article.other_write,
}) })
messages.success(self.request, _(u"New article '%s' created.") % self.newpath.article.current_revision.title) messages.success(self.request, _(u"New article '%s' created.") % self.newpath.article.current_revision.title)
transaction.commit()
# TODO: Handle individual exceptions better and give good feedback.
except Exception, e: except Exception, e:
transaction.rollback() log.exception("Exception creating article.")
if self.request.user.is_superuser: if self.request.user.is_superuser:
messages.error(self.request, _(u"There was an error creating this article: %s") % str(e)) messages.error(self.request, _(u"There was an error creating this article: %s") % str(e))
else: else:
messages.error(self.request, _(u"There was an error creating this article.")) messages.error(self.request, _(u"There was an error creating this article."))
transaction.commit()
return redirect('wiki:get', '') return redirect('wiki:get', '')
url = self.get_success_url() url = self.get_success_url()
transaction.commit()
return url return url
def get_success_url(self): def get_success_url(self):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment