130 lines
3.7 KiB
Python
130 lines
3.7 KiB
Python
from flask import request, redirect
|
|
|
|
|
|
from flask_admin import tools
|
|
from flask_admin._compat import text_type
|
|
from flask_admin.helpers import get_redirect_target, flash_errors
|
|
|
|
|
|
def action(name, text, confirmation=None):
|
|
"""
|
|
Use this decorator to expose actions that span more than one
|
|
entity (model, file, etc)
|
|
|
|
:param name:
|
|
Action name
|
|
:param text:
|
|
Action text.
|
|
:param confirmation:
|
|
Confirmation text. If not provided, action will be executed
|
|
unconditionally.
|
|
"""
|
|
def wrap(f):
|
|
f._action = (name, text, confirmation)
|
|
return f
|
|
|
|
return wrap
|
|
|
|
|
|
class ActionsMixin(object):
|
|
"""
|
|
Actions mixin.
|
|
|
|
In some cases, you might work with more than one "entity" (model, file, etc) in
|
|
your admin view and will want to perform actions on a group of entities simultaneously.
|
|
|
|
In this case, you can add this functionality by doing this:
|
|
1. Add this mixin to your administrative view class
|
|
2. Call `init_actions` in your class constructor
|
|
3. Expose actions view
|
|
4. Import `actions.html` library and add call library macros in your template
|
|
"""
|
|
|
|
def __init__(self):
|
|
"""
|
|
Default constructor.
|
|
"""
|
|
self._actions = []
|
|
self._actions_data = {}
|
|
|
|
def init_actions(self):
|
|
"""
|
|
Initialize list of actions for the current administrative view.
|
|
"""
|
|
self._actions = []
|
|
self._actions_data = {}
|
|
|
|
for p in dir(self):
|
|
attr = tools.get_dict_attr(self, p)
|
|
|
|
if hasattr(attr, '_action'):
|
|
name, text, desc = attr._action
|
|
|
|
self._actions.append((name, text))
|
|
|
|
# TODO: Use namedtuple
|
|
# Reason why we need getattr here - what's in attr is not
|
|
# bound to the object.
|
|
self._actions_data[name] = (getattr(self, p), text, desc)
|
|
|
|
def is_action_allowed(self, name):
|
|
"""
|
|
Verify if action with `name` is allowed.
|
|
|
|
:param name:
|
|
Action name
|
|
"""
|
|
return True
|
|
|
|
def get_actions_list(self):
|
|
"""
|
|
Return a list and a dictionary of allowed actions.
|
|
"""
|
|
actions = []
|
|
actions_confirmation = {}
|
|
|
|
for act in self._actions:
|
|
name, text = act
|
|
|
|
if self.is_action_allowed(name):
|
|
actions.append((name, text_type(text)))
|
|
|
|
confirmation = self._actions_data[name][2]
|
|
if confirmation:
|
|
actions_confirmation[name] = text_type(confirmation)
|
|
|
|
return actions, actions_confirmation
|
|
|
|
def handle_action(self, return_view=None):
|
|
"""
|
|
Handle action request.
|
|
|
|
:param return_view:
|
|
Name of the view to return to after the request.
|
|
If not provided, will return user to the return url in the form
|
|
or the list view.
|
|
"""
|
|
form = self.action_form()
|
|
|
|
if self.validate_form(form):
|
|
# using getlist instead of FieldList for backward compatibility
|
|
ids = request.form.getlist('rowid')
|
|
action = form.action.data
|
|
|
|
handler = self._actions_data.get(action)
|
|
|
|
if handler and self.is_action_allowed(action):
|
|
response = handler[0](ids)
|
|
|
|
if response is not None:
|
|
return response
|
|
else:
|
|
flash_errors(form, message='Failed to perform action. %(error)s')
|
|
|
|
if return_view:
|
|
url = self.get_url('.' + return_view)
|
|
else:
|
|
url = get_redirect_target() or self.get_url('.index_view')
|
|
|
|
return redirect(url)
|