Flask Admin-如何根据用户角色设置表单编辑规则或表单创建规则?

Flask Admin-如何根据用户角色设置表单编辑规则或表单创建规则?,flask,flask-admin,Flask,Flask Admin,我正在使用Flask和sqlite以及SQLAlchemy为中型组织制作简单的票务系统。对于数据的后端管理,我使用Flask Admin 用户和票证表如下所示: class User(db.Model, UserMixin): id = db.Column(db.Integer, primary_key=True) role = db.Column(db.Integer, default=0) vmc_kom = db.Column(db.String(20))

我正在使用Flask和sqlite以及SQLAlchemy为中型组织制作简单的票务系统。对于数据的后端管理,我使用Flask Admin

用户和票证表如下所示:

class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    role = db.Column(db.Integer, default=0)
    vmc_kom = db.Column(db.String(20))
    name = db.Column(db.String(30), nullable=False)
    phone = db.Column(db.String, default="not")
    email = db.Column(db.String(40), nullable=False)
    password = db.Column(db.String(60), nullable=False)
    tickets = db.relationship('Ticket', cascade="all,delete", backref='author', lazy=True)

    def __repr__(self):
        return f"('{self.name}')"

class Ticket(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key = True)
    title = db.Column(db.String(100), nullable=False)
    content = db.Column(db.Text, nullable=False)
    povod_vmc_kom = db.Column(db.String(20))
    osoba = db.Column(db.String(20), default="XYZ")
    dateVMC = db.Column(db.Date, nullable=False)
    deadline = db.Column(db.Date, nullable=False)
    is_finished = db.Column(db.Boolean, default = False)
    images = db.relationship('Image_ticket', cascade="all,delete", backref='home_ticket', lazy=True)
    solution = db.Column(db.Text)
    date_solution = db.Column(db.DateTime)
    zodpovedni = db.relationship("Zodpovedny", secondary="ticketutvary")
    sprava = db.Column(db.String(100))
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)

    def __repr__(self):
        return f"Ticket('{self.id}', '{self.title}', '{self.dateVMC}')"
我可以根据用户设置创建、编辑或删除票证的权限。
方法中设置的角色是可访问的

class TicketModelView(ModelView):
    column_list = ['id', 'title', 'osoba', 'content', 'povod_vmc_kom', 'dateVMC','zodpovedni', 'deadline', 'solution']
      def is_accessible(self):
        
        if current_user.is_authenticated and current_user.role == 0:
            self.can_export=True
            self.can_delete = False
            self.can_edit = False
            self.can_create = False
            return True
        
        if current_user.is_authenticated and current_user.role == 1:
            self.can_export=True
            self.can_delete=True
            return True
       
        if current_user.is_authenticated and current_user.role == 2:
            self.can_delete = False
            self.can_export=True
            return True
        
        if current_user.is_authenticated and current_user.role == 3:
            self.can_delete = False
            self.can_export=True

            return True
        return False
但我一直在努力为特定用户设置
表单编辑规则。例如,我希望允许角色==2的用户只编辑票证中的两列。当我将form_edit_规则直接放在ModelView类中时,它对每个人都有效。我也试过:

class TicketModelView(ModelView):
        column_list = ['id', 'title', 'osoba', 'content', 'povod_vmc_kom', 'dateVMC','zodpovedni', 'deadline', 'solution']
          def is_accessible(self):
            
            if current_user.is_authenticated and current_user.role == 2:
                self.can_export=True
                self.can_delete = False
                self.can_edit = False
                self.can_create = False
                self.form_edit_rules = ('zodpovedni','dateVMC')
                return True
但是没有成功

有人能把我推到正确的方向吗?我有什么遗漏吗?有没有使用过一些非常糟糕的练习


提前感谢。

表单编辑规则
已在调用方法
可访问时缓存。如果更新规则,则刷新缓存:

class TicketModelView(ModelView):

    column_list = ['id', 'title', 'osoba', 'content', 'povod_vmc_kom', 'dateVMC','zodpovedni', 'deadline', 'solution']

    def is_accessible(self):

        if current_user.is_authenticated and current_user.role == 2:
            self.can_export=True
            self.can_delete = False
            self.can_edit = False
            self.can_create = False
            self.form_edit_rules = ('zodpovedni','dateVMC')
            # Refresh form rules cache
            self._refresh_form_rules_cache()
            return True

        return False
也可以在运行时设置
form_edit_rules
,而无需使规则缓存失效。我将此SO Q/A用作以下内容的基础。如果用户已登录并具有角色
'admin'
,则他们可以查看并使用
'active'
字段

class AuthorView(sqla.ModelView):
    column_default_sort = ('last_name', False)
    column_searchable_list = ('first_name', 'last_name')

    @property
    def _form_edit_rules(self):
        return rules.RuleSet(self, self.form_edit_rules)

    @_form_edit_rules.setter
    def _form_edit_rules(self, value):
        pass

    @property
    def form_edit_rules(self):

        if not has_app_context() or current_user.has_role('admin'):
            return ('first_name', 'last_name', rules.Text(f'Authenticated User has Admin role'), 'active')

        return ('first_name', 'last_name', rules.Text('Not Authenticated and/or not Admin role'))
完整的单文件Python 3示例如下

requirements.txt

Babel==2.8.0
blinker==1.4
click==7.1.2
dnspython==2.0.0
email-validator==1.1.1
Faker==4.1.1
Flask==1.1.2
Flask-Admin==1.5.6
Flask-BabelEx==0.9.4
Flask-Login==0.5.0
Flask-Mail==0.9.1
Flask-Principal==0.4.0
Flask-Security==3.0.0
Flask-SQLAlchemy==2.4.4
Flask-WTF==0.14.3
idna==2.10
itsdangerous==1.1.0
Jinja2==2.11.2
MarkupSafe==1.1.1
passlib==1.7.2
python-dateutil==2.8.1
pytz==2020.1
six==1.15.0
speaklater==1.3
SQLAlchemy==1.3.18
text-unidecode==1.3
Werkzeug==1.0.1
WTForms==2.3.3
app.py

from datetime import datetime
from faker import Faker
import click
from flask import Flask, has_app_context, current_app
from flask_admin.form import rules
from flask_login import login_user, logout_user
from flask_security import UserMixin, RoleMixin, current_user, SQLAlchemyUserDatastore, Security
from flask_security.utils import hash_password
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin
from flask_admin.contrib import sqla

db = SQLAlchemy()


user_to_role = db.Table('user_to_role',
    db.Column('user_id', db.Integer(), db.ForeignKey('users.id')),
    db.Column('role_id', db.Integer(), db.ForeignKey('roles.id')))


class User(db.Model, UserMixin):
    __tablename__ = 'users'

    id = db.Column(db.Integer, primary_key=True)

    first_name = db.Column(db.Unicode(length=255), nullable=False)
    last_name = db.Column(db.Unicode(length=255), nullable=False, index=True)

    # Identification Data: email & password
    email = db.Column(db.Unicode(length=254), nullable=False, unique=True)
    password = db.Column(db.Unicode(length=255), nullable=False)
    active = db.Column(db.Boolean(), default=False)

    roles = db.relationship('Role', secondary=user_to_role, backref=db.backref('users', lazy='select'))


class Role(db.Model, RoleMixin):

    __tablename__ = 'roles'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode(length=64), unique=True)
    description = db.Column(db.Unicode(length=255), nullable=True)

    def __str__(self):
        return self.name


class Author(db.Model):
    __tablename__ = 'authors'

    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)
    active = db.Column(db.Boolean(), default=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'
app.config['SECURITY_PASSWORD_HASH'] = 'pbkdf2_sha512'
app.config['SECURITY_PASSWORD_SALT'] = 'c1b4797ffb4783bb4aed7e14a1494a01390eacf94ee324b9'

db.init_app(app)
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)


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

    """
        Create database
    """

    db.drop_all()
    db.create_all()
    _faker = Faker()

    security = current_app.extensions.get('security')
    _admin_role = security.datastore.find_or_create_role(name="admin", description='Administers the system')
    _user_role = security.datastore.find_or_create_role(name="user", description='Uses the system')

    users = [
        {'email': 'paul@example.net', 'first_name': 'Paul', 'last_name': 'Cunningham', 'password': hash_password('pa$$word'), 'role': _user_role},
        {'email': 'jane@example.net', 'first_name': 'Jane', 'last_name': 'Smith', 'password': hash_password('pa$$word'), 'role': _admin_role},
    ]

    for user in users:
        _role = user.pop('role')
        _user_db = security.datastore.create_user(**user)
        if _role:
            security.datastore.add_role_to_user(_user_db, _role)
            security.datastore.activate_user(_user_db)
            _user_db.confirmed_at = datetime.utcnow()

    security.datastore.commit()

    for _ in range(0, count):
        _author = Author(
            first_name=_faker.first_name(),
            last_name=_faker.last_name(),
            active=_faker.boolean()
        )
        db.session.add(_author)

    db.session.commit()


class AuthorView(sqla.ModelView):
    column_default_sort = ('last_name', False)
    column_searchable_list = ('first_name', 'last_name')

    @property
    def _form_edit_rules(self):
        return rules.RuleSet(self, self.form_edit_rules)

    @_form_edit_rules.setter
    def _form_edit_rules(self, value):
        pass

    @property
    def form_edit_rules(self):

        if not has_app_context() or current_user.has_role('admin'):
            return ('first_name', 'last_name', rules.Text(f'Authenticated User has Admin role'), 'active')

        return ('first_name', 'last_name', rules.Text('Not Authenticated and/or not Admin role'))


# Flask views
@app.route('/')
def index():
    _html = [
        '<a href="/impersonate-paul">Click me to get to impersonate Paul (user)!</a>',
        '<a href="/impersonate-jane">Click me to get to impersonate Jane (admin)!</a>'
    ]
    return '<br>'.join(_html)


@app.route('/impersonate-paul')
def impersonate_paul():
    _impersonate_user = User.query.filter(User.email == 'paul@example.net').first()
    logout_user()
    login_user(_impersonate_user)
    return '<a href="/admin/">Click me to get to Admin logged in as Paul (user)!</a>'


@app.route('/impersonate-jane')
def impersonate_jane():
    _impersonate_user = User.query.filter(User.email == 'jane@example.net').first()
    logout_user()
    login_user(_impersonate_user)
    return '<a href="/admin/">Click me to get to Admin logged in as Jane (admin)!</a>'


admin = Admin(app, template_mode="bootstrap3")
admin.add_view(AuthorView(Author, db.session))


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

谢谢你的回答。但是:“AttributeError:'NoneType'对象没有属性'is_authenticated'”。似乎您只能在“是否可访问”方法中接近“当前用户”。。。不知道为什么…@helepele-你是对的,声明为财产没有任何帮助。如果您更改了规则,则必须刷新缓存。非常感谢。完全错过了自我。_refresh_form_rules_cache()@helepele-显示如何使用属性更新的更新答案。
flask create-database --count 100