Flask Admin-如何根据用户角色设置form_edit_rules或form_create_rules? [英] Flask Admin - how to set form_edit_rules or form_create_rules based on role of user?
问题描述
我正在使用Flask和sqlite以及SQLAlchemy为中型组织制作简单的票务系统.对于数据的后端管理,我使用Flask-Admin.
I am making simple ticketing system for medium-sized organization using Flask and sqlite together with SQLAlchemy. For backend managing of data I use Flask-Admin.
用户和票证"表如下所示:
The User and Ticket table looks like this:
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}')"
我能够根据在 is_accesible
方法中设置的User.role设置创建,编辑或删除故障单的权限.
I was able to set permission to create, edit or delete Tickets based on User.role set in is_accesible
method.
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
但是我一直在努力为特定用户设置 form_edit_rules
.例如,我想允许角色== 2的用户仅编辑故障单中的两列.当我将form_edit_rules直接放在ModelView类中时,它适用于所有人.我也尝试过这个:
But I´ve been struggling really hard to set form_edit_rules
for specific user. For example I want to allow User with role == 2 to edit only two columns in Ticket. When I put form_edit_rules directly in ModelView Class it works but for everybody. I also tried this:
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
但没有成功.
任何人都可以将我推向正确的方向吗?有什么我想念的吗?是否有一些非常不好的做法?
Please can anyone push me right direction? Is there something I am missing? Is there some really bad practise used?
谢谢.
推荐答案
form_edit_rules
在调用 is_accessible
方法时已经被缓存.如果您更新规则,请刷新缓存:
form_edit_rules
is already cached by the time method is_accessible
is called. If you update the rules then refresh the cache:
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
,而无需使规则缓存无效.我使用了这样的Q/A flask-admin:如何仅允许超级用户查看指定的表列?以下的依据.如果用户登录并具有'admin'
角色,则他们可以查看并使用'active'
字段.
It is also possible to set the form_edit_rules
at runtime without resorting to invalidating the rules cache. I used this SO Q/A flask-admin: how to allow only super users can view the specified table column? as the basis for the following. If the user is logged in and is has role 'admin'
they can see and use the '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示例.
Full single file Python 3 example below.
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()
运行以下命令来初始化SQLite数据库.
Run the following command to initialize an SQLite DB.
flask create-database --count 100
这篇关于Flask Admin-如何根据用户角色设置form_edit_rules或form_create_rules?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!