I have two different roles setup on my Flask Python application, and I am using roles_required to force the user to be an admin to login or access the page. However, I am trying to work out how to use roles_required with two different roles...
I know there is a way to request a role and one of the two roles in a list. That looks like this:
roles_required('admin1', ['admin2'])
But what about role or other role?
Here is my problem:
My models:
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(128), nullable=False, unique=True)
password = db.Column(db.String(192), nullable=True, unique=True)
roles = db.relationship('Role', secondary='user_roles', backref=db.backref('users', lazy='dynamic'))
has_roles = lambda self, *args: set(args).issubset({role.name for role in self.roles})
class Role(db.Model):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(50))
class UserRoles(db.Model):
id = db.Column(db.Integer(), primary_key=True)
user_id = db.Column(db.Integer(), db.ForeignKey('user.id', ondelete='CASCADE'))
role_id = db.Column(db.Integer(), db.ForeignKey('role.id', ondelete='CASCADE'))
My route:
#mod_auth.route('/dashboard')
#login_required
# This line below doesn't seem to be working
#roles_required('admin1', 'admin2')
def dashboard():
return render_template('dashboard.html')
Related
So i been trying to make a like function for my q&a website. however, i'm stuck on database relations part of the models.py. I'm getting an error that says
"sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Triggering mapper: 'mapped class User->user'. Original exception was: Could not determine join condition between parent/child tables on relationship User.posts - there are multiple foreign key paths linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table."
This is my code for the user and post class
class Post(db.Model):
id = db.Column("id", db.Integer, primary_key=True)
title = db.Column("title", db.String(200))
text = db.Column("text", db.String(100))
date = db.Column("date", db.String(50))
#Create Foreign Key
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
comments = db.relationship("Comment", backref="post", cascade="all, delete-orphan", lazy=True)
recipient_id = db.Column(db.Integer, db.ForeignKey('user.id'))
likes = db.relationship('PostLike', backref='post', lazy='dynamic')
and my user class
class User(db.Model):
id = db.Column("id", db.Integer, primary_key=True)
first_name = db.Column("first_name", db.String(100))
last_name = db.Column("last_name", db.String(100))
email = db.Column("email", db.String(100))
password = db.Column(db.String(255), nullable=False)
registered_on = db.Column(db.DateTime, nullable=False)
posts = db.relationship("Post", backref="user", lazy=True)
comments = db.relationship("Comment", backref="user", lazy=True)
liked = db.relationship(
'PostLike',
foreign_keys='PostLike.user_id',
backref='user', lazy='dynamic'
)
def like_post(self, post):
if not self.has_liked_post(post):
like = PostLike(user_id=self.id, post_id=post.id)
db.session.add(like)
def unlike_post(self, post):
if self.has_liked_post(post):
PostLike.query.filter_by(
user_id=self.id,
post_id=post.id).delete()
def has_liked_post(self, post):
return PostLike.query.filter(
PostLike.user_id == self.id,
PostLike.post_id == post.id).count() > 0
my postlike class in the models.py
class PostLike(db.Model):
__tablename__ = 'post_like'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
my flask file for like_action function
#app.route('/like/<int:post_id>/<action>')
def like_action(post_id, action):
post = Post.query.filter_by(id=post_id).first_or_404()
if action == 'like':
session['user_id'].like_post(post)
db.session.commit()
if action == 'unlike':
session['user_id'].unlike_post(post)
db.session.commit()
return redirect(request.referrer)
You have two foreign keys pointing to User on Post:
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
recipient_id = db.Column(db.Integer, db.ForeignKey('user.id'))
so, your User doesn't know where to point
posts = db.relationship("Post", backref="user", lazy=True)
Have something like recipient = db.relationship (..) and author = db.relationship (..) in User model, and make posts = db.relationship("Post", back_populates="author", lazy=True).
I have built an app using flask, and am using flask-user to handle my users.
I am using a sqlite3 database. There is a problem with the functions to delete and add new users:
def delete_user(username):
if User.query.filter(User.username == username).first():
user = User.query.filter(User.username == username).first()
db.session.delete(user)
db.session.commit()
delete_user('bobby')
def create_user(username,password,role):
if not User.query.filter(User.username == username).first():
user = User(
username=username,
password=user_manager.hash_password(password)
)
user.roles.append(Role(name=role))
db.session.add(user)
db.session.commit()
create_user('ollie','password','admin')
It successfully deletes 'bobby' but doesn't add 'ollie'.
The create_user() does work, if the database is removed before the code is run. But not otherwise. I have also tried to comment out # db.create_all()
Before the functions above, is the models section:
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
active = db.Column('is_active', db.Boolean(), nullable=False, server_default='1')
username =.Column(db.String(50, collation='NOCASE'), nullable=False, unique=True)
password = db.Column(db.String(255), nullable=False, server_default='')
# Define the relationship to Role via UserRoles
roles = db.relationship('Role', secondary='user_roles')
# Define the Role data-model
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(50), unique=True)
# Define the UserRoles association table
class UserRoles(db.Model):
__tablename__ = 'user_roles'
id = db.Column(db.Integer(), primary_key=True)
user_id = db.Column(db.Integer(), db.ForeignKey('users.id', ondelete='CASCADE'))
role_id = db.Column(db.Integer(), db.ForeignKey('roles.id', ondelete='CASCADE'))
# Setup Flask-User
user_manager = UserManager(app, db, User)
db.create_all()
I'm trying to add RBAC to my existing flask application where I already have 2 models which describe User and Post model respectively. Here is my code:
# models.py
from datetime import datetime
from rpd_site import db, login_manager
from flask_login import UserMixin
# []
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
# Main site account table
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
image_file = db.Column(db.String(20), nullable=False, default='default.jpg')
password = db.Column(db.String(60), nullable=False)
confirmed = db.Column(db.Boolean, nullable=False, default=0)
posts = db.relationship('Post', backref='author', lazy=True)
def __repr__(self):
return f"User('{self.username}', '{self.email}', '{self.confirmed}')"
# Posts table
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.now) # current local time instead of .utcnow
content = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
image_file = db.Column(db.String(20), nullable=False, default='default_post.png')
def __repr__(self):
return f"Post('{self.title}', '{self.date_posted}', '{self.content[:15]}')"
When I tried add all missing code from here I faced with lots of errors. Especially I'm not sure if I should import UserMixin from flask_rbac or from flask_login.
Help me to understand how can I upgrade my DB with RBAC functionality.
This is a very broad question, I'll try to give you a minimum code so that you can achieve RBAC. Below example uses Flask-security.
from app import db
from flask_security import RoleMixin, UserMixin
# may to many association table between User and Role
roles_users = db.Table(
'roles_users',
db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
db.Column('role_id', db.Integer(), db.ForeignKey('role.id'))
)
class Role(db.Model, RoleMixin):
__tablename__ = 'role'
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(50), unique=True)
def __str__(self):
return self.name
class User(db.Model, UserMixin):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(50), unique=True)
password = db.Column(db.String(255))
roles = db.relationship('Role', secondary=roles_users,
backref=db.backref('users', lazy='joined'))
def __str__(self):
return self.email
You either migrate or create the DB based on above Models. The above will be sufficient for you to either perform back-end operation for RDBC or at view level.
You can then assign roles to each user easily using below link.
Flask-security create role,user and linking user_id to role_id
If you want to perform RBAC at view, follow below.
from flask_security import login_required, roles_accepted
#app.route('/a_restricted_view/')
#login_required
#roles_accepted('role_one', 'role_two')
def a_restricted_view():
return "I am only visible to users with role_one and role_two"
I'm developing a web app with python and flask. I use Flask, SQLAlchemy and PostgreSQL for development. I have many-to-one related models. By this models one company can have many users but each user can only have one company.
models.py
class Company(ResourceMixin, db.Model):
__tablename__ = 'companies'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), unique=True, index=True,
nullable=False, server_default='')
phone = db.Column(db.String(24))
email = db.Column(db.String(255), index=True)
address = db.Column(db.String(255))
# Relations
users = db.relationship('User', backref='company')
class User(UserMixin, ResourceMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
# User details
name = db.Column(db.String(50), index=True)
phone = db.Column(db.String(24))
address = db.Column(db.String(255))
email = db.Column(db.String(255), unique=True, index=True, nullable=False,
server_default='')
password = db.Column(db.String(128), nullable=False, server_default='')
# Relations
company_id = db.Column(db.Integer, db.ForeignKey('companies.id',
onupdate='CASCADE',
ondelete='SET NULL'),
index=True)
views.py
app.route('/')
def index():
company = Company.query.get(1)
flash(company.name, company.user_count)
return render_template('index.html')
Error summary: "user_count" attribute is not part of the Company model.
I want to get the number of the users dynamically from Company model. Attribute should count users on each call of the model and serve it on a regular attribute (like company.user_count). I made it by creating a class method and calling it in view function but i want it to make the process automatic without calling method prior to use attribute.
I tried init function like this:
def __init__(self):
self.user_count = len(self.users)
And like this:
def __init__(self):
self.status()
def status(self):
self.user_count = len(self.users)
return True
And like this:
def __init__(self):
self.status()
#classmethod
def status(self):
self.user_count = len(self.users)
return True
all three versions throws same error. How can i overcome the problem.
Thanks a lot!
You can use a property:
class User(Base):
...
#property
def user_count(self):
return len(self.users)
I want to create a relationship across a many-to-many table, a User has Roles which have a Project. I want a relationship in the User to all Projects related to its Roles. I tried much with primaryjoin and secondaryjoin but i don't get it working.
Here are my models:
roles_users = db.Table('roles_users',
db.Column('role_id', db.Integer, db.ForeignKey('role.id')),
db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
db.PrimaryKeyConstraint('role_id', 'user_id')
)
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.Unicode(80), unique=True, nullable=False)
roles = db.relationship('Role', secondary=roles_users, backref='users')
projects = db.relationship('Project' ???? )
class Role(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Unicode(80), unique=True, nullable=False)
project_id = db.Column(db.Integer, db.ForeignKey('project.id'), nullable=False)
class Project(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Unicode(80), nullable=False)
roles = db.relationship('Role', backref='project')
The problem is in your "references".
I refer you the following links that will help you to understand the constraints and relationship.
http://docs.sqlalchemy.org/en/rel_0_9/orm/relationships.html#many-to-many
This is the link which I found best for association.
http://docs.sqlalchemy.org/en/rel_0_9/orm/relationships.html#association-object