如何修改 Flask-Admin 列表中每个项目的可用操作

How to modify available actions for each item in a list in Flask-Admin

我想根据某些条件让一些行可编辑,而一些行只能查看,我该如何实现?

您可以通过覆盖 Flask-Admin list templatelist_row_actions 块来完成此操作。此块将调用视图中定义的方法,将操作和行(即模型)作为视图中定义的每个行操作的参数。方法 returns True 或 False 取决于是否允许该操作。下面是一个简单的例子。

在templates/admin目录中创建list.html:

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

{% block list_row_actions scoped %}

    {% for action in list_row_actions %}
        {% if admin_view.allow_row_action(action, row) %}
            {{ action.render_ctx(get_pk_value(row), row) }}
        {% endif %}
    {% endfor %}

{% endblock %}

注意 admin_view 是当前视图,我们创建了一个方法 allow_row_action 在视图中采用参数 actionrow(模型)。

在视图代码中定义一个mixin:

class RowActionListMixin(object):

    list_template = 'admin/list.html'

    def allow_row_action(self, action, model):
        return True

任何实现此 mixin 的视图都将使用上面定义的重写列表模板。它还定义了 allow_row_action 方法 returns True.

现在按如下方式定义要控制行操作的任何视图:

class Student1View(RowActionListMixin, sqla.ModelView):
    column_default_sort = ('last_name', False)
    column_searchable_list = ('first_name', 'last_name')
    column_filters = ('allow_edit', 'allow_delete')

    def _can_edit(self, model):
        # Put your logic here to allow edit per model
        # return True to allow edit
        return model.allow_edit

    def _can_delete(self, model):
        # Put your logic here to allow delete per model
        # return True to allow delete
        return model.allow_delete

    def allow_row_action(self, action, model):

        # # Deal with Edit Action
        if isinstance(action, EditRowAction):
            return self._can_edit(model)

        # # Deal with Delete Action
        if isinstance(action, DeleteRowAction):
            return self._can_delete(model)

        # # Deal with other actions etc

        # otherwise whatever the inherited method returns
        return super().allow_row_action()

注意被覆盖的 allow_row_method。它检查操作是什么并将决策传递给适当的本地方法。

这是一个自包含的单个文件示例,您还需要上面定义的列表模板。 Student 模型有两个字段 allow_editallow_delete 以及 first_namelast_name 字段。 allow_editallow_delete 允许您动态切换是否可以编辑 Student and/or 删除'。 Student1View 允许 editing/deleting 基于学生的 allow_editallow_delete 值。 Student2View 不会覆盖 allow_row_action,因此允许执行行操作,因为 RowActionListMixin class returns True.[= 中定义的基本方法38=]

from faker import Faker
import click
from flask import Flask
from flask_admin.model.template import EditRowAction, DeleteRowAction
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin
from flask_admin.contrib import sqla

db = SQLAlchemy()


class Student(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    first_name = db.Column(db.Text(length=255), nullable=False)
    last_name = db.Column(db.Text(length=255), nullable=False)
    allow_edit = db.Column(db.Boolean(), default=False, nullable=False)
    allow_delete = db.Column(db.Boolean(), default=False, nullable=False)

    def __str__(self):
        return f"ID: {self.id}; First Name: {self.first_name}; Last Name: {self.last_name}"


app = Flask(__name__)

app.config['SECRET_KEY'] = '123456790'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///sample.sqlite'

db.init_app(app)


@app.cli.command('create-database', short_help='Create student database')
@click.option('--count', default=100, help='Number of students (default 100)')
def create_database(count):

    """
        Create database with "count" students
    """

    db.drop_all()
    db.create_all()
    _faker = Faker()
    for _ in range(0, count):
        _student = Student(
            first_name=_faker.first_name(),
            last_name=_faker.last_name(),
            allow_edit=_faker.boolean(),
            allow_delete=_faker.boolean()
        )
        db.session.add(_student)

    db.session.commit()


class RowActionListMixin(object):

    list_template = 'admin/list.html'

    def allow_row_action(self, action, model):
        return True


class Student1View(RowActionListMixin, sqla.ModelView):
    column_default_sort = ('last_name', False)
    column_searchable_list = ('first_name', 'last_name')
    column_filters = ('allow_edit', 'allow_delete')

    def _can_edit(self, model):
        # Put your logic here to allow edit per model
        # return True to allow edit
        return model.allow_edit

    def _can_delete(self, model):
        # Put your logic here to allow delete per model
        # return True to allow delete
        return model.allow_delete

    def allow_row_action(self, action, model):

        # # Deal with Edit Action
        if isinstance(action, EditRowAction):
            return self._can_edit(model)

        # # Deal with Delete Action
        if isinstance(action, DeleteRowAction):
            return self._can_delete(model)

        # # Deal with other actions etc

        # otherwise whatever the inherited method returns
        return super().allow_row_action()


class Student2View(RowActionListMixin, sqla.ModelView):
    column_default_sort = ('last_name', False)
    column_searchable_list = ('first_name', 'last_name')
    column_filters = ('allow_edit', 'allow_delete')


# Flask views
@app.route('/')
def index():
    return '<a href="/admin/">Click me to get to Admin!</a>'


admin = Admin(app, template_mode="bootstrap3")
admin.add_view(Student1View(Student, db.session, name='Student 1', category='Students', endpoint='student-1'))
admin.add_view(Student2View(Student, db.session, name='Student 2', category='Students', endpoint='student-2'))


if __name__ == '__main__':
    app.run()