I've built a system that allows users to apply for code review and wait for manager to approve.
And now what I want to achieve is as below:
then all the fields become read-only(I manually set Project name as read-only here):
If it's rejected,

then all the fields become editable. Of course, when creating a new project, all the fields should be editable.
The code of class Project and ProjectView are as below:
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')
In my opinion, since approve is a boolean variable, there should be a condition judgement to tell if it is 0 or 1 and then the field become read-only or editable accordingly.
Thanks for any advise in advance.
As you already noticed setting readonly property for a field is rather simple but making it dynamic is a bit tricky.
First of all you need a custom field class:
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 attribute for your view:
class ProjectView(sqla.ModelView):
form_overrides = {
'project_name': ReadOnlyStringField
}
You need to pass custom readonly_condition function to ReadOnlyStringField instance. The easiest way I found is overriding edit_form method:
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
Happy coding!
The previous answer I put on here had a major flaw. The following uses a different approach by analyzing the form itself and adding readonly: True to render_kw for a particular form if a certain condition is met.
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
This is a bit hacky, and it requires that approved be in the edit form. If you used this solution, you could either add approved as a readonly field or, instead of readonly, you could remove the approved field from the form in the above class method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With