This question already has answers here:
sqlalchemy unique across multiple columns
(4 answers)
Closed 3 years ago.
I have my table like something below:
class Dummies(db.Model):
__tablename__ = 'dummies'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(100), index=True, nullable=True)
value = db.Column(db.String(100), index=True, nullable=True)
sum_id = db.Column(db.Integer, db.ForeignKey('pattern.id',
onupdate='CASCADE',
ondelete='CASCADE'),
index=True, nullable=False)
I am quite new to flask and trying to get around the syntax for this relationship, I want to represent. I want to use the name, value and sum_id to uniquely represent a row. Could someone help me how to write the relationship for this ?
If you want enforce the unique constraint on multiple columns you can explicitly set it like this:
__tablename__ = 'dummies'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=True)
value = db.Column(db.String(100), nullable=True)
sum_id = db.Column(db.Integer, db.ForeignKey('pattern.id'), nullable=False)
UniqueConstraint('name', 'value', 'sum_id'
name='myMultipleColumnUniqueConstraint')
This way you can enforce multiple column unique constraint. Also just wanted point out that
onupdate => refers to a default value for the column.
There is no ondelete in sqlAlchemy as far as I know. but you can use:
cascade = "all, delete-orphan" ```
on Parent Table relationship to achieve the same effect as ondelete="cascade" as in django.
class ParentDummies(db.Model):
dummies= relationship("Dummy", cascade="all, delete-orphan")
Also, if you are trying make those columns as composite primary key, you can do so like this:
``` class Dummies(db.Model):
__tablename__ = 'dummies'
id = db.Column(db.Integer)
name = db.Column(db.String(100), nullable=True)
value = db.Column(db.String(100), nullable=True)
sum_id = db.Column(db.Integer, db.ForeignKey('pattern.id', nullable=False),
PrimaryKeyConstrain('name', 'value', 'sum_id', name="myCompositePk")
OR
class Dummies(db.Model):
__tablename__ = 'dummies'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(100), nullable=True, primary_key=True)
value = db.Column(db.String(100), nullable=True, primary_key=True)
sum_id = db.Column(db.Integer, db.ForeignKey('pattern.id'), nullable=False, primary_key=True)
Related
In models defined using declarative, I have the following structure:
class ItemBin(Base, BaseModelMixin, PostCommitTriggersModelMixin):
__tablename__ = "item_bins"
id = Column(INTEGER(11), nullable=False, unique=True)
account_id = Column(INTEGER(11), primary_key=True, nullable=False, index=True)
sku = Column(String(200), primary_key=True, nullable=False, server_default=text("''"))
warehouse_id = Column(INTEGER(11), primary_key=True, nullable=False, server_default=text("'0'"))
location_id = Column(INTEGER(11), primary_key=True, nullable=False, index=True, server_default=text("'0'"))
bin = relationship("Bin", uselist=False, primaryjoin="foreign(ItemBin.location_id) == Bin.id")
class Bin(Base):
__tablename__ = "bins"
id = Column(Integer, nullable=False, unique=True, autoincrement=True)
account_id = Column(Integer, primary_key=True, nullable=False)
warehouse = Column(String(100), primary_key=True, nullable=False, server_default=text("''"), default="")
location = Column(String(100), primary_key=True, nullable=False, server_default=text("''"), default="")
The lack of FKs is by design.
Our code allows for orphan ItemBins to exist, they get picked up later on and a default parent is set on them, and the change gets logged. The problem is that when we tried to add a Bin.item_bins relationship like:
class Bin(...):
...
item_bins = relationship(ItemBin, primaryjoin="foreign(ItemBin.location_id) == Bin.id", lazy="dynamic")
Then when the code does item_bin.bin.delete() the system tries to set item_bin.location_id = NULL and that fails with "Dependency rule tried to blank-out primary key column item_bins.location_id..."
Is it possible to define a relationship and not have it update its children when the parent is deleted? We're making do without the relationship, but we're having to write repetitive queries to refer to the objects' children, instead of doing Bin.item_bins.any() and similar.
class Team(db.Model):
__tablename__ = "Teams"
id = db.Column(db.Integer, primary_key=True)
flashscore_id = db.Column(db.String(255), nullable=False, unique=True)
name = db.Column(db.String(255), nullable=False)
leagues = db.relationship("League",
secondary=league_teams_table)
standings = db.relationship('Standing', backref='teams', cascade="all,delete")
fixtures = db.relationship('Fixture', backref='teams', cascade="all,delete")
related_fixtures_table = db.Table('RelatedFixtures',
db.Column('fixture_id', db.Integer, db.ForeignKey('Fixtures.id')),
db.Column('related_fixture_id', db.Integer, db.ForeignKey('Fixtures.id')))
class Fixture(db.Model):
__tablename__ = "Fixtures"
id = db.Column(db.Integer, primary_key=True)
home_id = db.Column(db.Integer, db.ForeignKey('Teams.id'),
nullable=False)
away_id = db.Column(db.Integer, db.ForeignKey('Teams.id'),
nullable=False)
home_ref = db.relationship("Teams", backref="fixture", uselist=False, foreign_keys=[home_id])
away_ref = db.relationship("Teams", backref="fixture", uselist=False, foreign_keys=[away_id])
flashscore_id = db.Column(db.String(255), nullable=False, unique=True)
total_goals = db.Column(db.Integer, nullable=False)
total_fh_goals = db.Column(db.Integer, nullable=False, default=0)
total_sh_goals = db.Column(db.Integer, nullable=False, default=0)
total_home_goals = db.Column(db.Integer, nullable=False)
total_away_goals = db.Column(db.Integer, nullable=False)
total_home_fh_goals = db.Column(db.Integer, nullable=False, default=0)
total_home_sh_goals = db.Column(db.Integer, nullable=False, default=0)
total_away_fh_goals = db.Column(db.Integer, nullable=False, default=0)
total_away_sh_goals = db.Column(db.Integer, nullable=False, default=0)
related_fixtures = db.relationship("Fixture",
secondary=related_fixtures_table)
I wrote the above code trying to define multiple relations between Fixture & Team. When I run the application and execute an action I get the following error:
sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship Team.fixtures - 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.
I tried solving this by adding the following two lines:
home_ref = db.relationship("Teams", backref="fixture", uselist=False, foreign_keys=[home_id])
away_ref = db.relationship("Teams", backref="fixture", uselist=False, foreign_keys=[away_id])
But I still get the same error. Can anyone help me out?
You need to configure the fixtures relationship on the Team class, because that's what the error is telling you to do: Could not determine join condition ... on relationship Team.fixtures.
However, you can't fix this situation with foreign_keys; that expects a single (possibly compound) foreign key, and you have two, distinct foreign key relationships. You'd have to create two separate relationships, one for fixtures where this team is the home team, and another one for the away relationship.
If you want Team.fixtures to return a list of all Fixture rows that reference the team via one or both foreign keys, you need to create a custom primaryjoin condition, one that matches your Team.id against the home_id or the away_id column (and you probably want to add a DISTINCT condition to your query to avoid getting duplicate results for fixtures that connect to your team via both foreign keys).
You need to get rid of fixtures = db.relationship('Fixture', backref='teams', cascade="all,delete") in Team.
What you will have at the end is:
class Team(db.Model):
__tablename__ = "Teams"
id = db.Column(db.Integer, primary_key=True)
# ...
class Fixture(db.Model):
__tablename__ = "Fixtures"
id = db.Column(db.Integer, primary_key=True)
home_id = db.Column(db.Integer, db.ForeignKey('Teams.id'), nullable=False)
away_id = db.Column(db.Integer, db.ForeignKey('Teams.id'), nullable=False)
home_ref = db.relationship("Teams", backref="fixture", uselist=False, foreign_keys=[home_id])
away_ref = db.relationship("Teams", backref="fixture", uselist=False, foreign_keys=[away_id])
# ...
You have your foreign keys defined in home_id and away_id, then you 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. Refer to this section in the SQLAlchemy documentation.
I'm trying to create a set of tables which can all have a Contactassigned to them.
class Contact(Base):
__tablename__ = "contact"
id = Column(Integer, primary_key=True)
name = Column(String, index=True, nullable=False, doc="Name of the contact.")
phone = Column(String, index=True, doc="Phone number of the contact.")
Contacts can be linked to from various other tables, and one record can have more than one contact in different fields.
class BusinessGroup(Base):
__tablename__ = "business_group"
id = Column(Integer, primary_key=True)
name = Column(String, index=True, nullable=False, doc="Name of the group.")
main_contact = Column(Integer, ForeignKey("contact.id"), doc="Main contact details for the group.")
second_contact = Column(Integer, ForeignKey("contact.id"), doc="Second contact details for the group.")
class Vendor(Base):
__tablename__ = "vendor"
id = Column(Integer, primary_key=True)
name = Column(String, index=True, nullable=False, doc="Name of the vendor.")
contact = Column(Integer, ForeignKey("contact.id"), doc="Main contact details for the vendor.")
This setup seems to work, but in flask-admin, no contact fields show up when creating a new item for either BusinessGroup or Vendor.
How can I make this design work? Or should I be modelling this kind of relationship in a different way entirely?
I ended up subclassing the Contact table so that I could have:
class MainContact(Contact):
__tablename__ = "main_contact"
id = Column(Integer, ForeignKey("contact.id"), primary_key=True)
business_group = relationship("BusinessGroup", back_populates="main_contact")
business_group_id = Column(Integer, ForeignKey("business_group.id"), nullable=False)
...
class SecondContact(Contact):
__tablename__ = "second_contact"
id = Column(Integer, ForeignKey("contact.id"), primary_key=True)
business_group = relationship("BusinessGroup", back_populates="second_contact")
business_group_id = Column(Integer, ForeignKey("business_group.id"), nullable=False)
...
class BusinessGroup(Base):
__tablename__ = "business_group"
id = Column(Integer, primary_key=True)
name = Column(String, index=True, nullable=False, doc="Name of the group.")
main_contact = relationship(
"MainContact", back_populates="business_group", uselist=False,
doc="Main contact details for the business_group."
)
second_contact = relationship(
"SecondContact", back_populates="business_group", uselist=False,
doc="Second contact details for the business_group."
)
As well as requiring subclassing so that we can have two different contacts referred to from the same model, the other important part was adding the foreign key relationships, so that the contacts show up in the Flask Admin panel.
I am new using sqlAlchemy and having problem creating new tables, specially when it comes around 2 foreign keys pointing to 1 table:
class Offers(db.Model):
__tablename__ = 'offers'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
contact_ign = db.Column(db.String(100))
conversion_rate = db.Column(db.Float)
stock = db.Column(db.Integer)
create_date = db.Column(db.DateTime(timezone=True), default=func.now())
currency_pair = db.relationship('CurrencyPairs', backref='pair', lazy='dynamic')
class CurrencyPairs(db.Model):
__tablename__ = 'currency_pairs'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
league = db.Column(db.String(100))
pair_id = db.Column(db.Integer, db.ForeignKey('offers.id'))
want = db.relationship('Currency', backref='want', lazy='dynamic')
have = db.relationship('Currency', backref='have', lazy='dynamic')
class Currency(db.Model):
__tablename__ = 'currency'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(100), nullable=False)
poe_trade = db.Column(db.Integer, nullable=False)
poe_official = db.Column(db.String(10), nullable=False)
tier = db.Column(db.Integer, nullable=False)
want_id = db.Column(db.Integer, db.ForeignKey('currency_pairs.id'))
have_id = db.Column(db.Integer, db.ForeignKey('currency_pairs.id'))
The error I am getting is:
sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Triggering mapper: 'Mapper|CurrencyPairs|currency_pairs'. Original exception was: Could not determine join condition b
etween parent/child tables on relationship CurrencyPairs.want - 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 refe
rence to the parent table
I have try different things but I get same result.
What am I doing wrong?
Thanks In advance.
I know this is an old question, but I had the same problem. I hope to help others with the answer.
This issue is addressed in the sqlalchemy documentation.
https://docs.sqlalchemy.org/en/13/orm/join_conditions.html#handling-multiple-join-paths
class Offers(db.Model):
__tablename__ = 'offers'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
contact_ign = db.Column(db.String(100))
conversion_rate = db.Column(db.Float)
stock = db.Column(db.Integer)
create_date = db.Column(db.DateTime(timezone=True), default=func.now())
currency_pair = db.relationship('CurrencyPairs', backref='pair', lazy='dynamic')
class CurrencyPairs(db.Model):
__tablename__ = 'currency_pairs'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
league = db.Column(db.String(100))
pair_id = db.Column(db.Integer, db.ForeignKey('offers.id'))
want_currency = relationship("Currency", foreign_keys='[Currency.want_id]', back_populates="want_currency_pairs")
have_currency = relationship("Currency", foreign_keys='[Currency.have_id]', back_populates="have_currency_pairs")
class Currency(db.Model):
__tablename__ = 'currency'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(100), nullable=False)
poe_trade = db.Column(db.Integer, nullable=False)
poe_official = db.Column(db.String(10), nullable=False)
tier = db.Column(db.Integer, nullable=False)
want_currency_pairs = relationship(CurrencyPairs, foreign_keys="[Currency.want_id]", back_populates="want_currency")
have_currency_pairs = relationship(CurrencyPairs, foreign_keys="[Currency.have_id]", back_populates="have_currency")
The way you wrote the code, sqlalchemy can't really understand which relationship to choose, because you have 2 of the same relationship. So you have to describe to sqlalchemy that there are 2 relationships to the same table.
I'm trying to create 2 tables using SQLAlchemy models that are identical in structure but distinct in purpose, the Whitelist and Blacklist which also reference a Magazine (not shown). They both fkey to an instance of a Campaign (say a political campaign) which in turn has an fkey to a Politician model (also not shown).
I'm picturing the Whitelist/Blacklist to be many:1 to the Campaign since there could be multiple white/blacklists for, but when running the migration I get sqlalchemy.exc.ProgrammingError: (ProgrammingError) there is no unique constraint matching given keys for referenced table "campaigns". There also needs to be a M:1 from Campaign to Politician.
Can someone please explain why this is causing a unique constraint error since the whitelist and blacklist are on separate tables? Also, how would I be able to make this relational schema work?
class Campaign(Base):
__tablename__ = "campaigns"
id = Column(Integer, primary_key=True, nullable=False)
politician_id = Column(Integer, ForeignKey('politician.id'), nullable=False)
description = Column(Text, nullable=True)
class Whitelist(Base):
__tablename__ = "whitelist"
id = Column(Integer, primary_key=True, nullable=False)
campaign_id = Column(Integer, ForeignKey('campaign.id'), nullable=False)
magazine_id = Column(Integer, ForeignKey('magazine.id'), nullable=False)
class Blacklist(Base):
__tablename__ = "blacklist"
id = Column(Integer, primary_key=True, nullable=False)
campaign_id = Column(Integer, ForeignKey('campaign.id'), nullable=False)
magazine_id = Column(Integer, ForeignKey('magazine.id'), nullable=False)
It would seem that the relationship is indeed illegal because of the M:1 from the Whitelist/Blacklist to Campaign, while Campaign itself is a M:1 to Politician. I instead scrapped that approach and the models now look like:
class Campaign(Base):
__tablename__ = "campaigns"
id = Column(Integer, primary_key=True, nullable=False)
politician_id = Column(Integer, ForeignKey('politician.id'), nullable=False)
description = Column(Text, nullable=True)
class Listing(Base):
__tablename__ = "listings"
id = Column(Integer, primary_key=True, nullable=False)
campaign_id = Column(Integer, ForeignKey('campaign.id'), nullable=False)
magazine_id = Column(Integer, ForeignKey('magazine.id'), nullable=False)
listing_type = Column(String, nullable=False)
The original approach was intended to fit a little nicer into Flask-Admin but this way is more performant since it will reduce the additional query and join necessary in the previous incarnation. For those that may be thinking of this type of approach to use with Flask-Admin, you could use the inline_model() to make the UI a little cleaner and easier to manage.