Browse Source

API to control on transactions in admin views

Rafał Pitoń 10 years ago
parent
commit
6eaa21380f

+ 4 - 1
docs/developers/admin_actions.rst

@@ -55,7 +55,7 @@ Base class for lists if items. Supports following properties:
 * **items_per_page** - integer controlling number of items displayed on single page. Defaults to 0 which means no pagination
 * **SearchForm** - Form type used to construct form for filtering this list. Either this field or ``get_search_form`` method is required to make list searchable.
 * **ordering** - list of supported sorting methods. List of tuples. Each tuple should countain two items: name of ordering method (eg. "Usernames, descending") and ``order_by`` argument ("-username"). Defaults to none which means queryset will not be ordered. If contains only one element, queryset is ordered, but option for changing ordering method is not displayed.
-* **mass_actions** - list of dicts defining list's mass actions. Each dict should have ``action`` key that will be used to identify method to call, ``name`` for displayed name, ``icon`` for icon and optional ``confirmation`` message.
+* **mass_actions** - list of dicts defining list's mass actions. Each dict should have ``action`` key that will be used to identify method to call, ``name`` for displayed name, ``icon`` for icon and optional ``confirmation`` message. Actions can define optional "is_atomic" key to control if they should be wrapped in transaction or not. This is default behaviour for mass actions.
 * **selection_label** - Label displayed on mass action button if there are items selected. ``0`` will be replaced with number of selected items automatically.
 * **empty_selection_label** - Label displayed on mass action button if there are no items selected.
 
@@ -178,6 +178,9 @@ Once model instance is obtained either from database or empty instance is create
    While target argument value is always present, you don't have to do anything with it if its not making any sense for your view.
 
 
+In addition, views are wrapped in database transaction. To turn this behaviour off, define ``is_atomic`` attribute with value ``False``.
+
+
 Adding extra values to context
 ------------------------------
 

+ 12 - 2
misago/admin/views/generic/formsbuttons.py

@@ -6,12 +6,16 @@ from misago.admin.views.generic.base import AdminView
 
 
 class TargetedView(AdminView):
+    is_atomic = True
+
     def check_permissions(self, request, target):
         pass
 
     def get_target(self, kwargs):
         if len(kwargs) == 1:
-            select_for_update = self.get_model().objects.select_for_update()
+            select_for_update = self.get_model().objects
+            if self.is_atomic:
+                select_for_update = select_for_update.select_for_update()
             return select_for_update.get(pk=kwargs[kwargs.keys()[0]])
         else:
             return self.get_model()()
@@ -23,7 +27,13 @@ class TargetedView(AdminView):
             return None
 
     def dispatch(self, request, *args, **kwargs):
-        with transaction.atomic():
+        if self.is_atomic:
+            with transaction.atomic():
+                return self.wrapped_dispatch(request, *args, **kwargs)
+        else:
+            return self.wrapped_dispatch(request, *args, **kwargs)
+
+    def wrapped_dispatch(self, request, *args, **kwargs):
             target = self.get_target_or_none(request, kwargs)
             if not target:
                 messages.error(request, self.message_404)

+ 8 - 3
misago/admin/views/generic/list.py

@@ -209,17 +209,22 @@ class ListView(AdminView):
             raise MassActionError(_("You have to select one or more items."))
 
         action_queryset = context['items'].filter(pk__in=items)
+
         if not action_queryset.exists():
             raise MassActionError(_("You have to select one or more items."))
 
-        action_callable = getattr(self, 'action_%s' % action)
-        with transaction.atomic():
+        action_callable = getattr(self, 'action_%s' % action['action'])
+
+        if action.get('is_atomic', True):
+            with transaction.atomic():
+                return action_callable(request, action_queryset)
+        else:
             return action_callable(request, action_queryset)
 
     def select_mass_action(self, action):
         for definition in self.mass_actions:
             if definition['action'] == action:
-                return action
+                return definition
         else:
             raise MassActionError(_("Action is not allowed."))