Use Flask-SqlAlchemy to query relationship database - python

I'm trying to use Flask-SQLAlchemy to query out database for the user profile page
So far I don't have a solution for this problem, only able to query all the User data by using users.query.all()
Each user has their own role_id, department_id, researchfield_id.
How can i query out all the Role, Department, ResearchField data that has relationship with User through ID?
class User(UserMixin, db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
password_hash = db.Column(db.String(128))
is_admin = db.Column(db.Boolean, default=False)
department_id = db.Column(db.Integer, db.ForeignKey('departments.id'))
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
research_id = db.Column(db.Integer, db.ForeignKey('researchfields.id'))
class Department(db.Model):
__tablename__ = "departments"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(sqlalchemy.types.NVARCHAR(100), unique=True)
user = db.relationship('User', backref='department',
lazy='dynamic')
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(sqlalchemy.types.NVARCHAR(100), unique=True)
users = db.relationship('User', backref='role',
lazy='dynamic')
class ResearchField(db.Model):
__tablename__ = "researchfields"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(60), index=True)
parent_id = db.Column(db.Integer, db.ForeignKey("researchfields.id") , nullable=True)
users = db.relationship('User', backref='researchfield', lazy='dynamic')

If I understand correctly, what you're seeking for is a way to filter out users based on a specific model. Because in your example, the other way around is redundant - every user has only one department, so no need to filter out departments for that user. In order to achieve that, I would use the backref method provided by SQLAlchemy from the User model.
Here's an example consisting of two of the models:
from sqlalchemy.orm import backref
class User(UserMixin, db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
password_hash = db.Column(db.String(128))
is_admin = db.Column(db.Boolean, default=False)
department_id = db.Column(db.Integer, db.ForeignKey('departments.id'))
department = db.relationship("Department", backref=backref("users", lazy="dynamic"))
class Department(db.Model):
__tablename__ = "departments"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(sqlalchemy.types.NVARCHAR(100), unique=True)
Now you can use:
department = Department.query.filter_by(id=1).first()
print(department.users.filter_by(is_admin=True).all()) # get all admins with that department
Every user has only one department, so you could just get the user's department by:
user = User.query.filter_by(id=1).first()
print(user.department) # prints Department object

Related

SQLAlchemy Association Proxy distinct right side

Given the models below, how can I ensure that the Users returned by the Document.users association proxy are distinct?
For example, the DocumentUser association table can have the follow data
document_id
user_id
role_id
Document_1
User_1
Role_1
Document_1
User_1
Role_2
Document_2
User_2
Role_1
To demo, the models are laid out as:
class Document(db.Model):
__tablename__ = 'documents'
id = db.Column(db.String, primary_key=True)
title = db.Column(db.String)
document_user_associations = db.relationship(
'DocumentUser',
lazy='select',
back_populates='document'
)
users = association_proxy('document_user_associations', 'user')
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.String, primary_key=True)
full_name = db.Column(db.String)
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.String, primary_key=True)
name = db.Column(db.String)
class DocumentUser(db.Model):
__tablename__ = 'document_users'
document_id = db.Column(db.String, db.ForeignKey('documents.id') primary_key=True)
user_id = db.Column(db.String, db.ForeignKey('users.id'), primary_key=True)
role_id = db.Column(db.String, db.ForeignKey('roles.id'), primary_key=True)
document = db.relationship('Document', back_populates='document_user_associations')
user = db.relationship('User')
role = db.relationship('Role')
If I were to do the following, assuming the data in the above table is persisted,
document_1 = db.session.query(Document).get(1)
print(document_1.users)
I would hope to see [<User User_1>] but instead I get [<User User_1>, <User User_1>] due to the association object. Is it possible using the association_proxy to return distinct Users given the above model?
EDIT:
In effect, it would be ideal if the association_proxy, or the association table relationship it relies on, to be able to group by the user, providing a list of the roles.

Querying user specific data in flask

I'm building an app that shows a database of different plant species. Each user sees the same table of plant species except for the "notes" column, which each user can edit to their own liking. This is the database structure I have created:
class Note(db.Model):
__tablename__ = 'plantdatabasenote'
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.Text(), nullable=False)
plant_id = db.Column(db.Integer, db.ForeignKey("plant.id"))
user_id = db.Column(db.Integer, db.ForeignKey("profile.id"))
class Plant(db.Model):
__tablename__ = 'plant'
id = db.Column(db.Integer, primary_key=True)
common_name = db.Column(db.String(200), nullable=False)
date_created = db.Column(db.DateTime, default=datetime.utcnow)
notes = db.relationship("Note", backref="user_notes")
class Profile(db.Model):
__tablename__ = 'profile'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(200), unique=True, nullable=False)
I tried to retrieve the notes of user 1 with the following:
Plant.query.filter(Plant.notes.any(user_id=1)).all()
Unfortunately, this does not give me all the plants with the notes of user 1. Any idea how to fix this?

Multiple relationships to the same SQLAlchemy model

I have a User table that has role column. role can be student, professor or admin. User table have relation with Course table.
If relationship is between Course and student it should be many to many relation but if relationship is between Course and professor it should be one to many.
How to define multi relationship between two table with different roles?
User Model
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
firstname = db.Column(db.String(40))
lastname = db.Column(db.String(40))
username = db.Column(db.String(40), nullable=False, unique=True)
password_hash = db.Column(db.String(100), nullable=False)
role = db.Column(db.SmallInteger, nullable=False, default=0)
create_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
update_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
# what should i write instead of bellow lines?
# courses = db.relationship('Course', secondary=student_course, backref='students', lazy='dynamic')
# OR
# courses = db.relationship('Course', backref='teacher', lazy='dynamic')
ROLE_STUDENT = 0
ROLE_PROFESSOR = 1
ROLE_ADMIN = 2
Course Model
class Course(db.Model):
id = db.Column(db.Integer, primary_key=True)
teacher_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
title = db.Column(db.String(120))

Flask-SQLAlchemy Query records by relationship attribute

In the app I've been working on uses Flask-User and their suggested models for authorization control. Here's an example:
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
# User Authentication information
username = db.Column(db.String(50), nullable=False, unique=True)
password = db.Column(db.String(255), nullable=False, default='')
# User Email information
email = db.Column(db.String(255), nullable=False, unique=True)
confirmed_at = db.Column(db.DateTime())
# User information
is_enabled = db.Column(db.Boolean(), nullable=False, default=False)
# Relationships
roles = db.relationship('Role', secondary='user_roles',
backref=db.backref('users', lazy='dynamic'))
class Role(db.Model):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(50), unique=True)
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'))
In addition, as part of the Flask-User/Flask-Login, current_user gives me a User object corresponding to the current user of the app (if logged in) and, as defined in our model, current_user.roles returns a list of Role objects, corresponding to the roles that are bound to the current user by the UserRoles table.
Now, at some point, I want to be able to query all users that have any of the roles the current user has. How can that be achieved given our current_user.roles list and my models in SQL Alchemy? How can I select all user where any of its User.roles match any of our current_user.roles?
Add __repr__ to Role as follows:
class Role(db.Model):
#...
def __repr__(self):
return self.id
And you can use following query to achieve your requirement.
User.query.filter(User.roles.any(Role.id.in_([role.id for role in current_user.roles]))).all()

many to many relationship with three tables (sql-alchemy)

I am trying to get a many to many relationship working. I have three tables
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
class Groups(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(1000))
class Members(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
group_id = db.Column(db.Integer, db.ForeignKey('groups.id'))
I would like to have the option group.members, which should give me all User objects which are member of that group. I implemented it the following way
members = db.relationship('User', secondary="join(Members, User, Members.user_id == User.id)", primaryjoin="and_(Groups.id == Members.group_id)")
this seems to work, but when I delete a group it gives me (sometimes) the error
AttributeError: 'Join' object has no attribute 'delete'
so I guess this is not the right way to implement such a relation.
Any ideas how to do this correctly?
thanks
carl
Perhaps a simpler way to implement this is as follows (adapted from the documentation on Flask-SQLAlchemy
members = db.Table('members',
db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
db.Column('group_id', db.Integer, db.ForeignKey('groups.id'))
)
class Groups(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(1000))
members = db.relationship('User', secondary=members, backref=db.backref('group', lazy='dynamic'))
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
Instead of using a Model for the joining table (members), let's just use a simple table.
With this configuation, you can easily add/remove members and groups:
u = User(username='matt')
g = Groups(name='test')
db.session.add(u)
db.session.add(g)
db.session.commit()
g.members.append(u)
db.session.commit()
db.session.delete(g)
db.session.commit()

Categories