flask-admin:如何根据其他列的值使列只读?

时间:2017-04-05 05:43:20

标签: python flask flask-sqlalchemy flask-admin

我已经构建了一个允许用户申请代码审查并等待经理批准的系统。

现在我想要实现的目标如下:

  1. 如果已批准enter image description here 然后所有字段都变为只读(我手动将项目名称设置为只读):
  2. enter image description here

    1. 如果拒绝

      enter image description here

      然后所有字段都变为可编辑。当然,在创建新项目时,所有字段都应该是可编辑的 enter image description here

      课程ProjectProjectView的代码如下:

       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')
      

      在我看来,由于批准是一个布尔变量,所以应该有一个 条件判断判断它是0还是1然后该字段变为只读或相应的可编辑。

      感谢您提前提出建议。

3 个答案:

答案 0 :(得分:3)

正如您已经注意到,为某个字段设置readonly属性为rather simple但是将其设置为动态有点棘手。

首先,您需要一个自定义字段类:

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)

为您的观点设置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

快乐的编码!

答案 1 :(得分:2)

对我而言,这个技巧是最简单的方法:

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
        }
    }

答案 2 :(得分:1)

我在这里提出的上一个答案有一个重大缺陷。以下使用不同的方法,通过分析表单本身,并在满足特定条件时为特定表单添加readonly: Truerender_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字段方法。