actions.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. from flask import request, redirect
  2. from flask_admin import tools
  3. from flask_admin._compat import text_type
  4. from flask_admin.helpers import get_redirect_target, flash_errors
  5. def action(name, text, confirmation=None):
  6. """
  7. Use this decorator to expose actions that span more than one
  8. entity (model, file, etc)
  9. :param name:
  10. Action name
  11. :param text:
  12. Action text.
  13. :param confirmation:
  14. Confirmation text. If not provided, action will be executed
  15. unconditionally.
  16. """
  17. def wrap(f):
  18. f._action = (name, text, confirmation)
  19. return f
  20. return wrap
  21. class ActionsMixin(object):
  22. """
  23. Actions mixin.
  24. In some cases, you might work with more than one "entity" (model, file, etc) in
  25. your admin view and will want to perform actions on a group of entities simultaneously.
  26. In this case, you can add this functionality by doing this:
  27. 1. Add this mixin to your administrative view class
  28. 2. Call `init_actions` in your class constructor
  29. 3. Expose actions view
  30. 4. Import `actions.html` library and add call library macros in your template
  31. """
  32. def __init__(self):
  33. """
  34. Default constructor.
  35. """
  36. self._actions = []
  37. self._actions_data = {}
  38. def init_actions(self):
  39. """
  40. Initialize list of actions for the current administrative view.
  41. """
  42. self._actions = []
  43. self._actions_data = {}
  44. for p in dir(self):
  45. attr = tools.get_dict_attr(self, p)
  46. if hasattr(attr, '_action'):
  47. name, text, desc = attr._action
  48. self._actions.append((name, text))
  49. # TODO: Use namedtuple
  50. # Reason why we need getattr here - what's in attr is not
  51. # bound to the object.
  52. self._actions_data[name] = (getattr(self, p), text, desc)
  53. def is_action_allowed(self, name):
  54. """
  55. Verify if action with `name` is allowed.
  56. :param name:
  57. Action name
  58. """
  59. return True
  60. def get_actions_list(self):
  61. """
  62. Return a list and a dictionary of allowed actions.
  63. """
  64. actions = []
  65. actions_confirmation = {}
  66. for act in self._actions:
  67. name, text = act
  68. if self.is_action_allowed(name):
  69. actions.append((name, text_type(text)))
  70. confirmation = self._actions_data[name][2]
  71. if confirmation:
  72. actions_confirmation[name] = text_type(confirmation)
  73. return actions, actions_confirmation
  74. def handle_action(self, return_view=None):
  75. """
  76. Handle action request.
  77. :param return_view:
  78. Name of the view to return to after the request.
  79. If not provided, will return user to the return url in the form
  80. or the list view.
  81. """
  82. form = self.action_form()
  83. if self.validate_form(form):
  84. # using getlist instead of FieldList for backward compatibility
  85. ids = request.form.getlist('rowid')
  86. action = form.action.data
  87. handler = self._actions_data.get(action)
  88. if handler and self.is_action_allowed(action):
  89. response = handler[0](ids)
  90. if response is not None:
  91. return response
  92. else:
  93. flash_errors(form, message='Failed to perform action. %(error)s')
  94. if return_view:
  95. url = self.get_url('.' + return_view)
  96. else:
  97. url = get_redirect_target() or self.get_url('.index_view')
  98. return redirect(url)