I have a template that is using flask-login and I have a permission model built on bitsets. In my template, I want to expose content in a page based on permissions. I have a function can(permission)
in my user model that controls what a user can do.
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(64), unique=True)
fullname = db.Column(db.String(75))
password_hash = db.Column(db.String(128))
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
@property
def password(self):
raise AttributeError('password is not a readable attribute')
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
def can(self, permissions):
return self.role is not None and (self.role.permissions & permissions) == permissions
def __repr__(self):
return '<User: {}>'.format(self.email)
class Permission:
FOLLOW = 0x01
COMMENT = 0x02
WRITE_ARTICLES = 0x04
MODERATE_COMMENTS = 0x08
ADMINISTER = 0x80
When I try to call the can()
function in my template and pass it a permission, I get an error that Permission is not defined, presumably because the Permission class is not in scope for the template. Here is part of my base.html that all other templates extend:
{% if current_user.can(Permission.ADMINISTER) %}
<h3>Administration</h3>
<ul>
<li><a href="{{ url_for('main.add_user') }}">Add User</a></li>
<li><a href="{{ url_for('main.change_password') }}">Change Password</a></li>
<li><a href="{{ url_for('main.lock_user') }}">Lock User</a></li>
</ul>
{% endif %}
I'm loosely following Miguel Grinberg's book on Flask Web Development and he is doing the same thing in his base.html (line 32).
How can I make the Permission class available to my template?
Most obviously, but least conveniently, just pass the Permission
class when rendering a template that needs it.
return render_template('page.html', Permission=Permission)
Take this a step further by telling Flask to automatically add it to the template context. Decorate a context processor somewhere while setting up your app.
@app.context_processor
def include_permission_class():
return {'Permission': Permission}
Context is only avaialble to the immediate template being rendered for performance and clarity reasons. If you need it available in an {% import %}
or {% include %}
you could add with context
to each block that needs it: {% include 'other_template.html' with context%}
. Or, you could add the class to the Jinja env's global namespace.
app.add_template_global(Permission, 'Permission')
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