flask-admin: Как сделать столбцы доступными только для чтения в соответствии со значением других столбцов?


Я построил систему, которая позволяет пользователям подавать заявки на проверку кода и ждать одобрения менеджера.

И теперь то, чего я хочу достичь, выглядит следующим образом:

  1. , Если , утвержденной, Введите описание изображения здесь затем все поля становятся доступными только для чтения (я вручную устанавливаю имя проекта как доступное только для чтения здесь):

Введите описание изображения здесь

  1. Если это отклонено ,

    Введите описание изображения здесь

    Тогда все поля становятся редактируемый . Конечно, при создании нового проекта все поля должны быть доступны для редактирования.
    Введите описание изображения здесь

    Код класса 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 5

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 из формы в приведенном выше методе класса.