Querying data with nested relationships - python

I have a user table that has a relationship to my accounts table. My accounts table has a relationship to the account_permission table which is used to define permissions for accounts.
A user may have multiple accounts and he may have permissions to view his own accounts or other user's accounts.
I'm not sure how to query all the accounts that a user has and the accounts that he has permissions to view.
I thought that Account.query.filter_by(owner_id = user).all() would include permissions in it's results since it has a db relationship with a backref to the account_permission table but that is not the case.
# querying information
current_user = get_jwt_identity()
user = User.query.filter_by(email = current_user).first()
accounts = Account.query.filter_by(owner_id = user).all() # how do I get all the accounts that the user has permissions to?
# user model
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True, nullable=False)
password = db.Column(db.String(255), nullable=False)
isAdmin = db.Column(db.Boolean, default=False, nullable=False)
accounts = db.relationship('Account', backref='owner')
# accounts model
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from app import db, ma
class Account(db.Model):
account_id = db.Column(db.Integer, primary_key=True)
description = db.Column(db.String(255), nullable=False)
owner_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
permissions = db.relationship('AccountPermission', backref='permissions')
def __init__(self, account_id, description, owner_id):
self.account_id = account_id
self.description = description
self.owner_id = owner_id
class AccountSchema(ma.Schema):
class Meta:
fields = ('account_id', 'description', 'owner_id')
ordered = True
class AccountPermission(db.Model):
id = db.Column(db.Integer, primary_key=True)
account = db.Column(db.Integer, db.ForeignKey('account.account_id'))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
class AccountPermissionSchema(ma.Schema):
class Meta:
fields = ('account', 'user_id')
ordered = True

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.

How to have two relationships in one table SQLAlchemy

I'm struggling to configure my relationships for this database. I can't quite get my head around these relationships and foreign keys and how it all fits together.
I'm trying to have a user who can have multiple posts and multiple projects, but get "primaryjoin" errors ever since I had more than one relationship in the User class.
I would appreaciate any help, I know there have been similar questions asked but I can't seem to map those solutions to my own application.
Here is my models.py file:
from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin
from uuid import uuid4
import datetime
from api import app
db = SQLAlchemy(app)
def get_uuid():
return uuid4().hex
today = datetime.date.today()
next_week = today + datetime.timedelta(days=7)
class User(UserMixin, db.Model):
__tablename__ = "users"
id = db.Column(db.String(32), primary_key=True, unique=True, default=get_uuid)
name = db.Column(db.String(60))
email = db.Column(db.String(345), unique=True)
password = db.Column(db.Text, nullable=False)
projects = db.relationship("Project", backref="owner", primaryjoin="users.id == projects.owner_id")
posts = db.relationship("Post", backref="author", primaryjoin="users.id == posts.author_id")
class Project(db.Model):
__tablename__ = "projects"
id = db.Column(db.Integer, primary_key=True)
project_client = db.Column(db.String(60))
word_count = db.Column(db.Integer, nullable=False)
deadline = db.Column(db.DateTime, default=next_week)
completed = db.Column(db.Boolean, default=False)
owner_id = db.Column(db.String, db.ForeignKey("users.id"))
class Post(db.Model):
__tablename__ = "posts"
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(80))
views = db.Column(db.Integer)
author_id = db.Column(db.String, db.ForeignKey("users.id"))

How do I ensure that the user can follow of multiple contents like topic, user

I have a models like:
from app import db
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema, fields
from datetime import datetime
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, db.ForeignKey('follower.followable_id'), primary_key=True)
username = db.Column(db.String(32), nullable=False)
email = db.Column(db.String(320), nullable=False)
password_hash = db.Column(db.String(128), nullable=False)
password_salt = db.Column(db.String(22), nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow())
user_comment = db.relationship('UserComment', back_populates='author')
def to_json(self):
return {'username': self.username, 'email': self.email}
def get_id(self):
return self.id
class Topic(db.Model):
__tablename__ = 'topic'
id = db.Column(db.Integer, db.ForeignKey('follower.followable_id'), primary_key=True)
topic_name = db.Column(db.String(100))
class Follower(db.Model):
__tablename__ = 'follower'
id = db.Column(db.Integer, primary_key = True)
follower_id = db.Column(db.Integer, db.ForeignKey('user.id'))
followable_id = db.Column(db.Integer, db.ForeignKey('followable.followable_id'))
My goal is for the user to be able to follow topics and users. I tried to connect the id column of the user and topic table to the followable_id column of the follower table for this. And I linked the follower_id of the follower table to the user id.
In this case, I received an error when creating a topic table. (sqlalchemy.greetings.OperationalError: (pymysql.mistake.OperationalError) (1822, "Foreign key restriction could not be added. The missing index for the 'topic_ibfk_1' constraint in the referenced table is 'follower'")
https://stackoverflow.com/a/11618048/13975329 I took a reference from here and tried to create these tables but I couldn't.
How can I create a better design for user-topic follow mechanism and why does it give this error in topic table creation?

Use Flask-SqlAlchemy to query relationship database

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

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()

Categories