user1087973
user1087973

Reputation: 1108

Conditionally allow editing in the list view for Flask admin ModelView

I can have a property like so in the view class:

class MyExampleView(ModelView):

    @property
    def can_edit(self):
        # Return True or False based on some conditional logic

How can I access the row's attribute so I can for example conditionally show the edit column in the table, maybe if the row's is_active property is True or False.

Upvotes: 5

Views: 1927

Answers (1)

Sergey Shubin
Sergey Shubin

Reputation: 3257

You can make it with some template actions enhancements where you add a method to check row availability:

from flask_admin.model import template

class AccessMixin:
    def has_access(self, row):
        raise NotImplementedError()

class ViewRowAction(template.ViewRowAction, AccessMixin):
    def has_access(self, row):
        return True

class EditRowAction(template.EditRowAction, AccessMixin):
    def has_access(self, row):
        return row.is_active

class DeleteRowAction(template.DeleteRowAction, AccessMixin):
    def has_access(self, row):
        return row.is_active

Then you need to override list_row_actions block in list.html template to use this new method:

{% extends 'admin/model/list.html' %}

{% block list_row_actions scoped %}
  {% for action in list_row_actions %}
    {% if action.has_access(row) %}
      {{ action.render_ctx(get_pk_value(row), row) }}
    {% endif %}
  {% endfor %}
{% endblock %}

And then you need your model class to use the overridden row actions and list template:

from flask_admin.contrib.sqla.view import ModelView

class MyExampleView(ModelView):
    list_template = 'app_list.html'

    def get_list_row_actions(self):
        return (
            ViewRowAction(),
            EditRowAction(),
            DeleteRowAction(),
        )

Note that original get_list_row_actions method uses can_view_details, can_edit, can_delete, details_modal and edit_modal attributes to define the list of used actions. You discard this logic by overriding it.

Also it does not prevent user from actually editing or deleting objects (e.g. by manually typing the edit view URL in the browser). You need to implement access rights checking in the views methods for example:

from flask import flash, redirect, request
from flask_admin import expose
from flask_admin.babel import gettext
from flask_admin.helpers import get_redirect_target
from flask_admin.model.helpers import get_mdict_item_or_list

class MyExampleView(ModelView):
    @expose('/edit/', methods=('GET', 'POST'))
    def edit_view(self):
        """This code was copied from the
           flask_admin.model.base.BaseModelView.edit_view method"""
        return_url = get_redirect_target() or self.get_url('.index_view')

        id = get_mdict_item_or_list(request.args, 'id')
        if id is None:
            return redirect(return_url)

        model = self.get_one(id)

        if model is None or not model.is_active:
            flash(gettext('Record does not exist or you have 
                          not enough access rights.'), 'error')
            return redirect(return_url)

        return super(MyExampleView, self).edit_view()

Upvotes: 5

Related Questions