Relationship across a many-to-many table - python

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

Related

List of foreign keys in relationship table flask-sqlalchemy

Hi have two data models (User and Song) and I want users to have several playlists (many to many relationship).
The thing is, inside a playlist there is a user_id and multiple song_ids (the song list could even be empty). However I don't understand how I can make this happen.
Here's what I have so far.
(I removed some fields and the method to simplify)
class User(db.Model):
__tablename__ = "user"
_id = db.Column(db.Integer, primary_key=True)
_first_name = db.Column(db.String, nullable=False)
_last_name = db.Column(db.String, nullable=False)
_username = db.Column(db.String, unique=True, nullable=False)
_email = db.Column(db.String, unique=True, nullable=False)
_password = db.Column(db.String, nullable=False) #TODO: save hashed pwd
_is_admin = db.Column(db.Boolean, default=False)
_accesses = db.relationship(Song.__name__, secondary=Access, backref='_accesses')
_playlists = db.Column(db.JSON) # CHANGE THIS => relationship user-song (?)
class Song(db.Model):
__tablename__ = "song"
_id = db.Column(db.Integer, primary_key=True)
_title = db.Column(db.String, unique=True, nullable=False)
_lyrics = db.Column(db.ARRAY(db.String), nullable=False)
_creator_id = db.Column(db.Integer, nullable=False) #TODO: add user_fk
_accesses = db.relationship(User.__name__, secondary=Access, backref='_accesses')
I've tried to create a relationship table but I just don't understand how to have multiple songs inside the relationship.
Should it be something like this?
playlists = db.Table(
db.Column('id', db.Integer, primary_key=True),
db.Column('name', db.String, nullable=False),
db.Column('user_id', db.Integer, db.ForeignKey(user_fk), nullable= False),
db.Column('songs_ids', db.ARRAY(db.Integer, db.ForeignKey(song_fk))),
db.Column('created_at', db.Datetime, default=datetime.now),
)
Or should I make a relationship song-playlist before making the playlist relationship?

Sqlalchemy delete orphaned relation with one-to-many

I have two tables, User and Profiles. A user can have many profiles. If the user is deleted all the profiles are deleted. However I want to also make it so if a user has no profiles the user is also delete (assume I always insert a user with at least one profile).
I looked into delete-orphan but I'm not totally sure where I should put this or if it does what I need?
Any help is appreciated.
https://docs.sqlalchemy.org/en/13/orm/cascades.html#delete-orphan
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
active = db.Column(db.Boolean(), default=True, nullable=False)
created_date = db.Column(db.DateTime, default=func.now(), nullable=False)
profile_ = db.relationship("Profile", back_populates="user_", cascade="all,delete")
class Profile(db.Model):
__tablename__ = 'profile'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
user_ = db.relationship("User", back_populates="profile_")

Foreign keys, relationship between models in sqlalchemy flask

I have these two models:
class User(db.Model, UserMixin):
__tablename__ = 'users'
__table_args__ = (
PrimaryKeyConstraint('id',),
)
id = db.Column(db.Integer, primarky_key=True)
username = db.Column(db.String, unique=True, nullable=False)
email = db.Column(db.String, unique=True, nullable=False)
password = db.Column(db.String, nullable=False)
class Review(db.Model):
__tablename__ = 'reviews'
__table_args__ = (
PrimaryKeyConstraint('id', ),
)
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.Text, nullable=False)
I wanted to create a relationship between these two tables so that when a user writes a review it should go to Review model and it has a relation with user_id in Users model. I have tried lots of answers but they are returning different kinds of errors I am confused, can anybody help?
You can use db.relationship to create a one-to-many relationship between users and reviews.
class User(db.Model, UserMixin):
__tablename__ = 'users'
__table_args__ = (
PrimaryKeyConstraint('id',),
)
id = db.Column(db.Integer, primarky_key=True)
username = db.Column(db.String, unique=True, nullable=False)
email = db.Column(db.String, unique=True, nullable=False)
password = db.Column(db.String, nullable=False)
reviews = db.relationship('Review', backref='user', cascade='all, delete, delete-orphan')
class Review(db.Model):
__tablename__ = 'reviews'
__table_args__ = (
PrimaryKeyConstraint('id', ),
)
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
This would allow for user.reviews and review.user
Try this:
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
user = db.relationship(User, backref='user_reviews', lazy=True)
And read the documentation.

Query a single table with relationship database Flask-SQLAlchemy

I've got three tables: User, Role, Department
I want to query all the user with selected following filters:
Department.name , Role.name and User.degree
I can't find a solution to this problem, any suggestion would be great!
Here's my models simplified:
class User(UserMixin, db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
password_hash = db.Column(db.String(128))
degree = db.Column(db.String(20),default=None)
department_id = db.Column(db.Integer, db.ForeignKey('departments.id'))
role_id = db.Column(db.Integer, db.ForeignKey('roles.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')
session.query(User).filter(
User.department.has(name='some_name)).filter(
User.role.has(name='some_role')).filter(
User.degree == 'some_degree')
It's a simple query with joins. You can modify "department" with your department filter and "role" with the same. You should modify the select part (session.query(User.id)) with the fields you want.
users = (session.query(User.id).join(Department, Department.user == User.id).join(Role, Role.user == User.id).filter(Department.name=="department").filter(Role.name=="role").group_by(User.id))

How to set one to many and one to one relationship at same time in Flask-SQLAlchemy?

I'm trying to create one-to-one and one-to-many relationship at the same time in Flask-SQLAlchemy. I want to achieve this:
"A group has many members and one administrator."
Here is what I did:
class Group(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(140), index=True, unique=True)
description = db.Column(db.Text)
created_at = db.Column(db.DateTime, server_default=db.func.now())
members = db.relationship('User', backref='group')
admin = db.relationship('User', backref='admin_group', uselist=False)
def __repr__(self):
return '<Group %r>' % (self.name)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
group_id = db.Column(db.Integer, db.ForeignKey('group.id'))
admin_group_id = db.Column(db.Integer, db.ForeignKey('group.id'))
created_at = db.Column(db.DateTime, server_default=db.func.now())
However I got an error:
sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join
condition between parent/child tables on relationship Group.members -
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.
Does anyone know how to do that properly?
The solution is to specify the foreign_keys argument on all relationships:
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
group_id = Column(Integer, ForeignKey('groups.id'))
admin_group_id = Column(Integer, ForeignKey('groups.id'))
class Group(Base):
__tablename__ = 'groups'
id = Column(Integer, primary_key=True)
members = relationship('User', backref='group', foreign_keys=[User.group_id])
admin = relationship('User', backref='admin_group', uselist=False, foreign_keys=[User.admin_group_id])
Perhaps consider the admin relation in the other direction to implement "a group has many members and one admin":
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
group_id = Column(Integer, ForeignKey('groups.id'))
group = relationship('Group', foreign_keys=[group_id], back_populates='members')
class Group(Base):
__tablename__ = 'groups'
id = Column(Integer, primary_key=True)
members = relationship('User', foreign_keys=[User.group_id], back_populates='group')
admin_user_id = Column(Integer, ForeignKey('users.id'))
admin = relationship('User', foreign_keys=[admin_user_id], post_update=True)
See note on post_update in the documentation. It is necessary when two models are mutually dependent, referencing each other.
The problem you're getting comes from the fact that you've defined two links between your classes - a User has a group_id (which is a Foreign Key), and a Group has an admin (which is also defined by a Foreign Key). If you remove the Foreign Key from the admin field the connection is no longer ambiguous and the relationship works. This is my solution to your problem (making the link one-to-one):
from app import db,app
class Group(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(140), index=True, unique=True)
description = db.Column(db.Text)
created_at = db.Column(db.DateTime, server_default=db.func.now())
admin_id = db.Column(db.Integer) #, db.ForeignKey('user.id'))
members = db.relationship('User', backref='group')
def admin(self):
return User.query.filter_by(id=self.admin_id).first()
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), unique=True)
group_id = db.Column(db.Integer, db.ForeignKey('group.id'))
created_at = db.Column(db.DateTime, server_default=db.func.now())
The one drawback to this is that the group object doesn't have a neat admin member object you can just use - you have to call the function group.admin() to retrieve the administrator. However, the group can have many members, but only one of them can be the administrator. Obviously there is no DB-level checking to ensure that the administrator is actually a member of the group, but you could add that check into a setter function - perhaps something like:
# setter method
def admin(self, user):
if user.group_id == self.id:
self.admin_id = user.id
# getter method
def admin(self):
return User.query.filter_by(id=self.admin_id).first()
Ok, I found a workaround for this problem finally. The many-to-many relationship can coexist with one-to-many relationship between the same two tables at the same time.
Here is the code:
groups_admins = db.Table('groups_admins',
db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
db.Column('group_id', db.Integer, db.ForeignKey('group.id'))
)
class Group(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(140), index=True, unique=True)
description = db.Column(db.Text)
created_at = db.Column(db.DateTime, server_default=db.func.now())
members = db.relationship('User', backref='group')
admins = db.relationship('User',
secondary=groups_admins,
backref=db.backref('mod_groups', lazy='dynamic'),
lazy='dynamic')
def __repr__(self):
return '<Group %r>' % (self.name)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
group_id = db.Column(db.Integer, db.ForeignKey('group.id'))
created_at = db.Column(db.DateTime, server_default=db.func.now())
I still want someone to tell me how to set one-to-many and one-to-one relationship at the same time, so I leave my answer here and won't accept it forever.
This link solved it for me
most important thing is to specify foreign_keys value in the relation as well as the primary join

Categories

Resources