flask sqlalchemy multiple foreign keys in relationship - python

Hi I'm using flask and sqlalchemy, I'm trying to get the matches relationship in Team to get all matches whether or not it is team1 or team2 (so what i want is to be able to get all matches for given team through the matches attribute regardless if it is team1 or team2 in the Match table), I get the error:
sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship Team.matches
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.
class Match(db.Model):
id = db.Column(db.Integer, primary_key=True)
map = db.Column(db.String(80), index=True)
date = db.Column(db.DateTime, index=True)
agreed = db.Column(db.Boolean)
done = db.Column(db.Boolean)
ladder_id = db.Column(db.Integer, db.ForeignKey('ladder.id'))
team1_id = db.Column(db.Integer, db.ForeignKey('team.id'))
team2_id = db.Column(db.Integer, db.ForeignKey('team.id'))
team1_rounds = db.Column(db.Integer)
team2_rounds = db.Column(db.Integer)
team1_accepted_score = db.Column(db.Boolean)
team2_accepted_score = db.Column(db.Boolean)
points = db.Column(db.Integer)
team1 = db.relationship('Team', foreign_keys='Match.team1_id')
team2 = db.relationship('Team', foreign_keys='Match.team2_id')
class Team(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), index=True)
tag = db.Column(db.String(20), index=True, unique=True)
captain_id = db.Column(db.Integer, db.ForeignKey('user.id'))
captain = db.relationship('User', uselist=False,
foreign_keys='Team.captain_id')
members = db.relationship('User', backref='team',
foreign_keys='User.team_id', lazy='dynamic')
matches = db.relationship('Match',
foreign_keys='[Match.team1_id, Match.team2_id]', lazy='dynamic')

I got help from the guys on #sqlalchemy irc so i was going with the wrong approach I've now set up a relationship with a primaryjoin instead:
matches = db.relationship('Match', primaryjoin="or_(Team.id==Match.team1_id, Team.id==Match.team2_id)", lazy='dynamic')

Related

SQLAlchemy ORM: Mapping a non-unique column to schema

How would you map a column that is not unique and is not a key into another schema(table)?
class TaskEntity(Base, BaseMixin):
__tablename__ = "task_entity"
__table_args__ = (UniqueConstraint("dag_no", "ordinal_position", name="dag_ordinal_uq_constraint"),)
task_no = Column(BIGINT(20), primary_key=True, autoincrement=True, nullable=False)
job_no = Column(BIGINT(20), ForeignKey("job_tb.job_no"), nullable=False)
task_name = Column(String(128), unique=True, nullable=False)
ordinal_position = Column(SMALLINT(6), nullable=False, default=1)
ordinal_position is not unique on its own, but is unique per task_no which is unique per job_no.
Ex) job_no.A can only have 1 of task_no.A which can only have 1 of ordinal_position.C. But job_no.B can have a task_no.A and ordinal_position.C.
I am trying to create the below schema in conjunction with class TaskEntity above, but am returning a "errno: 150 "Foreign key constraint is incorrectly formed" which I am assuing comes from the fact that ordinal_position is not unique.
class TaskLog(Base, BaseMixin):
__tablename__ = "task_log"
task_log_no = Column(BIGINT(20), nullable=False, autoincrement=True, primary_key=True)
execution_datetime = Column(TIMESTAMP, nullable=False)
start_datetime = Column(TIMESTAMP, nullable=False)
duration = Column(Float, nullable=False)
job_no = Column(BIGINT(20), ForeignKey("job_tb.job_no"), nullable=False)
task_no = Column(BIGINT(20), ForeignKey("task_entity.task_no"), nullable=False)
task_name = Column(String(128), ForeignKey("task_entity.task_name"), nullable=False)
# this is the declaration causing issues:
task_ordinal_position = Column(SMALLINT(6), ForeignKey("task_entity.ordinal_position"), nullable=False)
Have tried using relationships and "primary_join=", but the mapping seems to be very off once the data comes in.
Any inputs, much appreciated.
If I'm reading this correctly, then you probably want a UniqueConstraint across all three columns:
__table_args__ = (
UniqueConstraint('task_no', 'job_no', 'ordinal_position'),
)

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 ?

Sqlalchemy and many to many relationship linking two tables

I am having trouble creating a link between the Orders and ProductOrders table with Sqlalchemy. I need to link order_id, which exists in both Orders table and ProductOrders table in order to query orders from a store and also get the product data as well. I am working with BigCommerce API so order_id is only unique to each specific store and not unique globally like Shopify API.
The order_id is the link I wish to create, but it only unique for each store.id in the Store table so I can't do a simple join as the order_id is no unique on its own. Do I use the Store table to link this many to many relationship, or do I need to use the StoreUser table that is already linking the store_id and user_id together? I am so confused. Any help would be greatly appreciated.
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
bc_id = db.Column(db.Integer, nullable=False)
email = db.Column(db.String(120), nullable=False)
storeusers = relationship("StoreUser", backref="user")
class StoreUser(db.Model):
id = db.Column(db.Integer, primary_key=True)
store_id = db.Column(db.Integer, db.ForeignKey('store.id'), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
admin = db.Column(db.Boolean, nullable=False, default=False)
class Store(db.Model):
id = db.Column(db.Integer, primary_key=True)
store_hash = db.Column(db.String(16), nullable=False, unique=True)
access_token = db.Column(db.String(128), nullable=False)
scope = db.Column(db.Text(), nullable=False)
admin_storeuser_id = relationship("StoreUser",
primaryjoin="and_(StoreUser.store_id==Store.id, StoreUser.admin==True)")
storeusers = relationship("StoreUser", backref="store")
store_orders_id = relationship("Orders",
primaryjoin="(Orders.store_id==Store.id)")
store_orders = relationship("Orders", backref="store", cascade="all, delete")
store_product_orders_id = relationship("ProductOrders",
primaryjoin="(ProductOrders.store_id==Store.id)")
store_product_orders = relationship("ProductOrders", backref="store", cascade="all, delete")
class Orders(db.Model):
id = db.Column(db.Integer, primary_key=True)
store_id = db.Column(db.Integer, db.ForeignKey('store.id'), nullable=False)
order_id = db.Column(db.Integer,nullable=False)
date_created = db.Column(db.DateTime, nullable=False)
class ProductOrders(db.Model):
id = db.Column(db.Integer, primary_key=True)
store_id = db.Column(db.Integer, db.ForeignKey('store.id'), nullable=False)
order_id = db.Column(db.Integer, nullable=False)
product_id = db.Column(db.Integer, nullable=False)
base_price = db.Column(db.Float, nullable=False)
I think you would need a multi-column primary key, so the unique constraint is order_id+store_id
This answer shows multi column primary keys in sqlalchemy: https://stackoverflow.com/a/9036128/210101
And this one shows joining on multiple columns: https://stackoverflow.com/a/51164913/210101

Flask-Sqlalchemy: filter rows where column contains a certain object

I have the following database setup:
abcs = db.Table('abcs',
db.Column('hero_id', db.Integer, db.ForeignKey('hero.id')),
db.Column('player_id', db.Integer, db.ForeignKey('player.id')),
db.Column('game_id', db.Integer, db.ForeignKey('game.id'))
)
class Player(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(250), nullable=False)
account_id = db.Column(db.String(250), nullable=False, unique=True)
class Hero(db.Model):
id = db.Column(db.Integer, primary_key=True)
hero_id = db.Column(db.Integer, unique=True)
localized_name = db.Column(db.String)
url_small_portrait = db.Column(db.String)
url_large_portrait = db.Column(db.String)
url_full_portrait = db.Column(db.String)
url_vertical_portrait = db.Column(db.String)
class Game(db.Model):
id = db.Column(db.Integer, primary_key=True)
mmr = db.Column(db.Integer)
server_steam_id = db.Column(db.Integer)
match_id = db.Column(db.Integer)
lobby_id = db.Column(db.Integer)
activate_time = db.Column(db.Integer)
deactivate_time = db.Column(db.Integer)
duration = db.Column(db.Integer)
has_pro = db.Column(db.Integer, default=-1)
radiant_win = db.Column(db.Integer)
players = db.relationship("Player", secondary = abcs, lazy='subquery',
backref=db.backref('games', lazy=True))
heroes = db.relationship("Hero", secondary = abcs, lazy='subquery',
backref=db.backref('games', lazy=True))
Which means whenever I add an entry to the table 'Game', Game.players will return a list with objects of the type 'Player'.
Now what I would like to do is to make a query of the form
p = Player.query.filter(P.account_id == 12345).first()
games = Game.query.filter(*get all games where Game.players contain p*).all()
My first naive approach for the 2nd line was
games = Game.query.filter(Game.players.contains(p)).all()
and that DOES return all entries from 'Game' where Game.players contains p, BUT for some reason, using .contains changes the order of elements in every row's .players, so for example the list
games[0].players
has now a different order compared to the situation where I would query that particular game directly.
So basically, I'm asking how I can achieve what I would like to do - how can I query all games in the table Game that contain a specific Player?
Use order_by in your queries to guarantee you get a predictable order. For example
games = Game.query.filter(Game.players.contains(p)).order_by(Game.some_field).all()

Child with multiple foreign keys to parent?

I have a company with a country code, a business ID, and a list of employees:
class Company(db.Model):
id = db.Column(db.Integer, primary_key=True)
business_id = db.Column(db.String(20), nullable=False)
country_code = db.Column(db.String(2), nullable=False)
employee = db.relationship('Employee', backref='company')
class Employee(db.Model):
id = db.Column(db.Integer, primary_key=True)
company_id = db.Column(db.Integer, db.ForeignKey('company.id'))
But say the data changes contantly, and the id isn't always the same for the same company (a company might get removed from the database, then re-added later on with different id). However, the combination of country_code and business_id is always quaranteed to be both unique, and constant for the same company.
How do I create two foreign keys for Employee that point into Company's country_code and business_id?
I tried doing this through an older StackOverflow question, but it raised an error that 'str' shouldn't be used:
class Company(db.Model):
id = db.Column(db.Integer, primary_key=True)
business_id = db.Column(db.String(20), nullable=False)
country_code = db.Column(db.String(2), nullable=False)
employee = db.relationship('Employee', backref='company', foreign_keys=['Company.business_id', 'Company.country_code'])
However, Python's syntax prevents me from referring to the class itself.
I am not sure exactly what you are asking, but the code sample below does two things, which I think would be helpful:
define a composite foreign key
indicate which foreign key to use for the relationship between Company and Employee
Model:
class Company(db.Model):
id = db.Column(db.Integer, primary_key=True)
business_id = db.Column(db.String(20), nullable=False)
country_code = db.Column(db.String(2), nullable=False)
name = db.Column(db.String(), nullable=False)
employee = db.relationship(
'Employee', backref='company',
# 2. indicate FK to use below
foreign_keys=lambda: [Employee.business_id, Employee.country_code],
# foreign_keys=lambda: Employee.company_id,
)
class Employee(db.Model):
id = db.Column(db.Integer, primary_key=True)
company_id = db.Column(db.ForeignKey('company.id'))
name = db.Column(db.String(), nullable=False)
business_id = db.Column(db.String(20), nullable=False)
country_code = db.Column(db.String(2), nullable=False)
# 1. define a composite foreign key
__table_args__ = (
db.ForeignKeyConstraint(
['business_id', 'country_code'],
['company.business_id', 'company.country_code']
),
)

Categories

Resources