SqlAlchemy - Nested relationship loading behaviour - python

I use SqlAlchemy to manage queries with my database, but i have a problem.
I have 3 Tables defined here:
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
orders = relationship("Order", backref=backref("payer", uselist=True, lazy='dynamic'), foreign_keys="Order.user_id", order_by="desc(Order.date)")
gifts = relationship("Order", backref=backref("receiver", uselist=False), foreign_keys="Order.user_target_id", order_by="desc(Order.date)")
class Order(db.Model):
id = db.Column(db.Integer, primary_key=True)
date = db.Column(db.DateTime)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
user_target_id = db.Column(db.Integer, db.ForeignKey('user.id'))
partner_id = db.Column(db.Integer, db.ForeignKey('partner.id'))
class Partner(db.Model):
id = db.Column(db.Integer, primary_key=True)
orders = relationship("Order", backref=backref("partner", uselist=False))
I would like to get all the "gifts" for a user, and for each gift (an Order object) i expect to get the 'payer', 'receiver' and 'partner' relations filled.
I am really new with SqlAlchemy so maybe that i have badly configured my relationships.
Can somebody help me ?

Related

Many to many relationship in flask SQLAlchemy

I'm learning flask and i'm looking to implement a many to many realtionship. I searched on internet but there are different ways,
this is what i have tried so far
class Group(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
class Course(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
class Group_Course(db.Model):
id = db.Column(db.Integer, primary_key=True)
group_id = db.Column(db.Integer, db.ForeignKey(Group.id))
theory_id = db.Column(db.Integer, db.ForeignKey(Course.id))
I'm not sure this is the right way to do it. I'm having issues with delete endpoint. I think that i should add on delete cascade but i don't know how to do it.
there are some sites that add relationships to the table association, so the association table looks like that.
class Group_Course(db.Model):
id = db.Column(db.Integer, primary_key=True)
group_id = db.Column(db.Integer, db.ForeignKey(Group.id))
course_id = db.Column(db.Integer, db.ForeignKey(Course.id))
course = db.relationship(Course, backref="course",cascade='all,
delete')
group = db.relationship(Group, backref="group", cascade='all,
delete`)
there are another examples where they are including a relationship field in both tables like that:
class Group(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
courses = db.relationship("Course", secondary=course_groups,
back_populates="courses")
class Course(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
groups = db.relationship("Group", secondary=course_groups,
back_populates="groups")
So i'm confused, which one is the most correct ?

Flask sqlalchemy how to manage additional information in a many-to-many relationship

Consider the following many-to-many relationship:
class Hashes(db.Model):
id = db.Column(db.Integer, primary_key=True)
hash = db.Column(db.String, nullable=False, unique=True)
xref_hashes_users = db.Table("xref_hashes_users",
db.Column('hash', db.ForeignKey('hashes.id'), primary_key=True),
db.Column('user', db.ForeignKey('user.id'), primary_key=True))
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String)
hashes = db.relationship("Hashes", secondary="xref_hashes_users", backref="users")
Let's say I want to allow users to store some additional information about their hashes, perhaps a label. It makes sense to me that given this is a user-specific piece of information about a hash, it would go in the association table like:
xref_hashes_users = db.Table("xref_hashes_users",
db.Column('hash', db.ForeignKey('hashes.id'), primary_key=True),
db.Column('user', db.ForeignKey('user.id'), primary_key=True),
db.Column('label', db.String))
Given this schema, is it possible to use the ORM to add/remove/update labels? How would I do this?
It looks like the answer is to use an Association Object to store the extra data.
https://docs-sqlalchemy.readthedocs.io/ko/latest/orm/basic_relationships.html#association-object
So my example becomes:
class Hashes(db.Model):
id = db.Column(db.Integer, primary_key=True)
hash = db.Column(db.String, nullable=False, unique=True)
users = db.relationship("UserHashAssociation", back_populates="hashes")
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String)
hashes = db.relationship("UserHashAssociation", back_populates="users")
class UserHashAssociation(db.Model):
__tablename__ = "xref_hashes_users"
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), primary_key=True)
hash_id = db.Column(db.Integer, db.ForeignKey("hashes.id"), primary_key=True)
label = db.Column(db.String)
users = db.relationship("User", back_populates="hashes")
hashes = db.relationship("Hashes", back_populates="users")
Then I am able to update the label like:
hash = Hashes(hash=hash_string)
user_hash_association = UserHashAssociation(label="foo", user_id=user.id)
hash.users.append(user_hash_association)
db.session.add(hash)
db.session.commit()

Flask-SQLAlchemy Multiple Databases to form a query

I am new to using Flask SQLAlchemy and I have a few questions on how can how can I link separate databases together to form a query.
I have 3 databases: User, Homework, Questions, where 1 user can have many homework and 1 homework can have many questions. I have made 3 separate .py files for the 3 classes and each file calls simple functions like query.all(), and some simple filtering.
I have previously learned MySQL and I am wondering how can I create an SQLAlchemy equivalent of the following query:
SELECT * FROM user, homework, questions
WHERE user.user_id = homework.user_id
AND homework.homework_id = questions.homework_id
My question is how do I go about achieving this? Do I make a new file and find a way to bind these databases together or is there a more optimal way of doing it?
Also, how do I go about retrieving aggregated function values? For instance, the average marks one would get for each homework.
User.py
class User(db.Model):
__tablename__ = 'user'
user_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(20), nullable=False)
Homework.py
class Homework(db.Model):
__tablename__ = 'homework'
homework_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
user_id= db.Column(db.Integer, nullable=False)
subject = db.Column(db.String(20), nullable=False)
Question.py
class Question(db.Model):
__tablename__ = 'question'
question_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
homework_id = db.Column(db.Integer, nullable=False)
marks = db.Column(db.Integer, nullable=False)
You can create relationships amongst your tables using the following
class User(db.Model):
__tablename__ = 'user'
user_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(20), nullable=False)
class Homework(db.Model):
__tablename__ = 'homework'
homework_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.user_id'), nullable=False)
subject = db.Column(db.String(20), nullable=False)
class Question(db.Model):
__tablename__ = 'question'
question_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
homework_id = db.Column(db.Integer, db.ForeignKey('homework.homework_id'), nullable=False)
marks = db.Column(db.Integer, nullable=False)
Then you can access info from a given table using the following
insert your desired user_id in replace for xxx
user_object = User.query.filter_by(user_id=xxx).first()
username = user_object.username
user_id_value = user_object.user_id
homework_object = Homework.query.filter_by(user_id=user_id_value).first()
subject = homework_object.subject
homework_id_value = homework_object.homework_id
question_object = Question.query.filter_by(homework_id=homework_id_value).first()
marks = question_object.marks

Flask SQLAlchemy: Child table with multiple parents?

I'm new to flask_sqlalchemy, and while I understand how the one-to-many, and many-to-many relationships work, I'm struggling to understand how I can apply them to my specific data types. I have the following three tables: TeamStat, PlayerStat, and Stat which are loosely described as follows
class PlayerStat(db.Model):
id = db.Column(db.Integer, primary_key=True)
date = db.Column(db.DateTime, nullable=False, default=datetime.datetime)
player_id = db.Column(db.Integer, db.ForeignKey('player.player_id'), nullable=False)
class TeamStat(db.Model):
id = db.Column(db.Integer, primary_key=True)
date = db.Column(db.DateTime, nullable=False, default=datetime.datetime)
team_id = db.Column(db.Integer, db.ForeignKey('team.team_id'), nullable=False)
class Stat(db.Model):
id = db.Column(db.Integer, primary_key=True)
value = db.Column(db.String(80), nullable=False)
Since Stat is a generic table type, I would like to use it by both the PlayerStat table (for individual player stats), as well as the TeamStat table (as a sum of the stats for all players on a team). Can someone help me understand how I can refer one child table to multiple parent tables in this fashion?
Use association tables to bridge your stat table to your players and team tables. This example is pretty close to what you are already doing, except the date column is moved on to the stat record table and I've replaced your PlayerStat and TeamStat objects with unmapped tables.
I've assumed you have two ORM classes, Player and Team (not Flask-SQLAlchemy sorry but concept remains the same):
plr_stat_assc = Table('plr_stat_assc', Base.metadata,
Column('player_id', Integer, ForeignKey('player.id')),
Column('stat_id', Integer, ForeignKey('stat.id'))
)
team_stat_assc = Table('team_stat_assc', Base.metadata,
Column('team_id', Integer, ForeignKey('team.id')),
Column('stat_id', Integer, ForeignKey('stat.id'))
)
class Player(Base):
__tablename__ = 'player'
id = Column(Integer, primary_key=True)
stats = relationship("Stat", secondary=plr_stat_assc)
class Team(Base):
__tablename__ = 'team'
id = Column(Integer, primary_key=True)
stats = relationship("Stat", secondary=team_stat_assc)
class Stat(Base):
__tablename__ = 'stat'
id = Column(Integer, primary_key=True)
date = Column(DateTime, nullable=False, default=datetime.datetime)
value = Column(String(80), nullable=False)

SQLAlchemy multiple joins to single table

I am making a wishlist app and I want to have db schema like bellow, but I can't figure out how to make the joins in sqlalchemy (this is the first time I am using sqlalchemy).
DB schema
(user : wish = 1 : N)
When I select a user, I want to get a list of wishes and each wish may contain a different user (an arranger of the wish)
So I could do something like this
first_user = User.query.get(1)
user_wishes = first_user.wishes.all()
for wish in user_wishes:
if wish.arranger is not None:
print(wish.id, wish.owner.id, wish.arranger.id)
else:
print(wish.id, wish.owner.id)
I have looked up some tutorials, but I only found simple relations.
I need a relation from User to Wish and in the Wish, back to both the UserWishOwner (the user from which I got here) a UserWishArranger (if there is any).
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
wishes = db.Column(db.relationship('Wish', backref='owner', lazy='dynamic'))
class Wish(db.Model):
id = db.Column(db.Integer, primary_key=True)
owner_id = db.Column(db.Integer, db.ForeignKey('user.id'), index=True)
arranger_id = db.Column(db.Integer, db.ForeignKey('user.id'), index=True)
arranger = relationship("User", foreign_keys=[arranger_id])
I have come up with some code, but am a bit confused, because owner_id and arranger_id are the same...
What do I need to do, to make this work?
Just like this
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
wishes = db.relationship('Wish', backref='owner', lazy='dynamic', foreign_keys="[Wish.owner_id]")
class Wish(db.Model):
id = db.Column(db.Integer, primary_key=True)
owner_id = db.Column(db.Integer, db.ForeignKey('user.id'), index=True)
arranger_id = db.Column(db.Integer, db.ForeignKey('user.id'), index=True)
arranger = db.relationship("User", foreign_keys=[arranger_id])

Categories

Resources