flask-admin: Как сделать столбцы доступными только для чтения в соответствии со значением других столбцов?
Я построил систему, которая позволяет пользователям подавать заявки на проверку кода и ждать одобрения менеджера.
И теперь то, чего я хочу достичь, выглядит следующим образом:
- , Если , утвержденной, затем все поля становятся доступными только для чтения (я вручную устанавливаю имя проекта как доступное только для чтения здесь):
-
Если это отклонено ,
Тогда все поля становятся редактируемый . Конечно, при создании нового проекта все поля должны быть доступны для редактирования.
Код класса
Project
иProjectView
выглядит следующим образом:from flask_sqlalchemy import SQLAlchemy from flask_admin.contrib import sqla from flask_security import current_user # Create Flask application app = Flask(__name__) app.config.from_pyfile('config.py') db = SQLAlchemy(app) class Project(db.Model): id = db.Column(db.Integer, primary_key=True) project_name = db.Column(db.Unicode(128)) version = db.Column(db.Unicode(128)) SVN = db.Column(db.UnicodeText) approve = db.Column(db.Boolean()) def __unicode__(self): return self.name class ProjectView(sqla.ModelView): def is_accessible(self): if not current_user.is_active or not current_user.is_authenticated: return False return False @property def _form_edit_rules(self): return rules.RuleSet(self, self.form_rules) @_form_edit_rules.setter def _form_edit_rules(self, value): pass @property def _form_create_rules(self): return rules.RuleSet(self, self.form_rules) @_form_create_rules.setter def _form_create_rules(self, value): pass @property def form_rules(self): form_rules = [ rules.Field('project_name'), rules.Field('version'), rules.Field('SVN'), ] if not has_app_context() or current_user.has_role('superuser'): form_rules.append('approve')
На мой взгляд, поскольку approve являетсябулевой переменной, то должно быть условие суждения, чтобы сказать, если это 0 или 1, а затем поле становится доступным только для чтения или редактирования соответственно.
Спасибо за любые советы заранее.
3 ответа:
Как вы уже заметили, установка свойства
readonly
для поля довольно проста, но сделать его динамическим немного сложно.Прежде всего вам нужен пользовательский класс полей:
from wtforms.fields import StringField class ReadOnlyStringField(StringField): @staticmethod def readonly_condition(): # Dummy readonly condition return False def __call__(self, *args, **kwargs): # Adding `readonly` property to `input` field if self.readonly_condition(): kwargs.setdefault('readonly', True) return super(ReadOnlyStringField, self).__call__(*args, **kwargs) def populate_obj(self, obj, name): # Preventing application from updating field value # (user can modify web page and update the field) if not self.readonly_condition(): super(ReadOnlyStringField, self).populate_obj(obj, name)
Set form_overrides атрибут для вашего представления:
class ProjectView(sqla.ModelView): form_overrides = { 'project_name': ReadOnlyStringField }
Вам нужно передать пользовательскую функцию
readonly_condition
в экземплярReadOnlyStringField
. Самый простой способ, который я нашел, это переопределение метода edit_form :class ProjectView(sqla.ModelView): def edit_form(self, obj=None): def readonly_condition(): if obj is None: return False return obj.approve form = super(ProjectView, self).edit_form(obj) form.project_name.readonly_condition = readonly_condition return form
Счастливого кодирования!
Для меня этот трюк был самым простым способом сделать это:
from flask_sqlalchemy import SQLAlchemy from flask_admin.contrib.sqla import ModelView from flask_admin.form.rules import Field class Example(db.Model): id = db.Column(db.Integer, primary_key=True) not_editable = db.Column(db.Unicode(128)) editable = db.Column(db.Unicode(128)) class ReadonlyFiledRule(Field): def __call__(self, form, form_opts=None, field_args={}): field_args['readonly'] = True return super(ReadonlyFiledRule, self).__call__(form, form_opts, field_args) class ExampleView(ModelView): form_edit_rules = (ReadonlyFiledRule('not_editable'), 'editable', )
Обновление (самый простой способ):
class ExampleView(ModelView): form_widget_args = { 'not_editable': { 'readonly': True } }
Предыдущий ответ, который я дал здесь, имел серьезный недостаток. Ниже используется другой подход, анализируя саму форму и добавляя
readonly: True
кrender_kw
для конкретной формы, если выполняется определенное условие.class ProjectView(sqla.ModelView): # ... other class code def edit_form(self, obj=None): # grab form from super form = super(ProjectView, self).edit_form(obj) # form.approved.data should be the same as approved # if approved is included in the form if form.approved.data: if form.project_name.render_kw: form.project_name.render_kw.update({ 'readonly': True }) else: form.project_name.render_kw = {'readonly': True} return form
Это немного банально, и это требует, чтобы
approved
было в форме редактирования. Если вы используете это решение, вы можете либо добавитьapproved
в качестве поляreadonly
, либо вместоreadonly
удалить полеapproved
из формы в приведенном выше методе класса.