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