form_edit_rules
已经被 time 方法缓存了is_accessible
叫做。如果更新规则则刷新缓存:
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/AFlask-admin:如何只允许超级用户可以查看指定表列?作为以下的基础。如果用户已登录并且具有角色'admin'
他们可以看到并使用'active'
field.
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': '[email protected]', 'first_name': 'Paul', 'last_name': 'Cunningham', 'password': hash_password('pa$$word'), 'role': _user_role},
{'email': '[email protected]', '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 == '[email protected]').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 == '[email protected]').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()
运行以下命令来初始化 SQLite 数据库。
flask create-database --count 100