Search code examples
pythonflaskflask-admin

flask admin - move delete button to edit view


I'm new to flask admin and I need to move the delete button to the edit view.

Here is the AdminModelView class that my other views are inheriting from.

class AdminModelView(sqla.ModelView):
    can_view_details = True


    # column_extra_row_actions = [ViewRowAction()]

    def is_accessible(self):
        if not current_user.is_active or not current_user.is_authenticated:
            return False
        if current_user.can('administrator'):
            return True
        return False

    def _handle_view(self, name, **kwargs):
        """
        Override builtin _handle_view in order to redirect users when a view is not accessible.
        """
        if not self.is_accessible():
            if current_user.is_authenticated:
                # permission denied
                abort(403)
            else:
                # login
                return redirect(url_for('auth.login', next=request.url))
            print(self._edit_form_class)


    def get_list_row_actions(self):
        """
            Return list of row action objects, each is instance of
            :class:`~flask_admin.model.template.BaseListRowAction`
        """
        actions = []

        if self.can_view_details:
            if self.details_modal:
                actions.append(template.ViewPopupRowAction())
            else:
                actions.append(template.ViewRowAction())

        return actions + (self.column_extra_row_actions or [])

I've redefined get_list_row_actions to take the edit and delete buttons off of the list view. I'm wondering if there's a part of my AdminModelView class that I can change or if I need to change the template for the edit form.


Solution

  • My solution:

    Wrote new endpoint on class AdminModelView(sqla.ModelView):

    @expose('/delete', methods=('DELETE',))
    def delete_view(self):
        """
            Delete model
        """
        id = request.args.get('id')
        if id is None:
            return jsonify({"success": False}), 404
        model = self.get_one(id)
        db.session.delete(model)
        db.session.commit()
        flash("{0} deleted".format(model))
        return jsonify({"success": True}), 200
    

    Used these macros to render a delete button and a modal to the edit view template.

    {% if admin_view.can_delete and model %}
      {{ add_modal_button(url='#', title='Delete', content='Delete', modal_window_id='delete_modal', btn_class='btn btn-danger') }}
    {% endif %}
    
    {% macro delete_modal() %}
    <div class="modal fade" id="delete_modal" tabindex="-1" role="dialog">
      <div class="modal-dialog" role="document">
          {# bootstrap version > 3.1.0 required for this to work #}
        <div class="modal-content">
          <div class="panel panel-danger">
            <div class="panel-heading">Delete Record</div>
            <div class="panel-body">
              <p>You are about to delete this record. This action cannot be undone.</p>
              <p>Would you like to proceed?</p>
            </div>
            <div class="panel-footer text-center">
              <button type="button" class="btn btn-secondary" id="cancel_delete">Cancel</button>
              <button type="button" class="btn btn-danger" id="confirm_delete">Delete</button>
            </div>
          </div>
        </div>
      </div>
    </div>
    {% endmacro %}
    

    Hybrid Jinja2 and JavaScript. This should be refactored. {{model.id}} can be stored in the DOM and retrieved with jQuery before the AJAX request is made.

    $("#cancel_delete").click(function() {
      $("#delete_modal").modal("toggle")
    });
    
    $("#confirm_delete").click(function() {
      $.ajax({
        url: "../delete?id={{ model.id }}",
        method: "DELETE"
      }).done(function() {
        window.location.replace("{{ return_url }}");
      }).fail(function() {
        $(".actions-nav").before(
          '<div class="alert alert-danger alert-dismissable fade in">' +
          '<a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>' +
          '<strong>Error:</strong> This object failed to delete.' +
          '</div>'
        )
      })
    })