SQLAlchemy association confusion - python

I have a student database in SQLAlchemy with the following tables.
class Marks(Base):
__table_args__ = {'extend_existing': True}
__tablename__ = 'marks'
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
mark = Column(Integer, nullable=False, unique=False)
subject_fk = Column(Integer, ForeignKey('subjects.id'))
subject = relationship('Subjects', foreign_keys=[locale_fk],uselist=False,
backref='marks', innerjoin=False, post_update=False)
class Subjects(Base):
__table_args__ = {'extend_existing': True}
__tablename__ = 'subjects'
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
subject = Column(String(10, convert_unicode=True), nullable=False, unique=True)
class StudentIds(Base):
__table_args__ = {'extend_existing': True}
__tablename__ = 'student_ids'
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
student_id = Column(String(50, convert_unicode=True), nullable=False,unique=False)
junctionid = relationship("Junction", cascade='save-update',
backref='student_ids', lazy='select', post_update=True)
mk=relationship("Marks",secondary=lambda:junction_table)
marks = association_proxy('mk', 'mark')
class Junction(Base):
__table_args__ = {'extend_existing': True}
__tablename__ = "junction_table"
student_id_fk = Column(Integer, ForeignKey('student_ids.id'), primary_key=True)
mark_fk = Column(Integer, ForeignKey('marks.id'), primary_key=True)
mark = relationship('Marks', cascade='save-update', backref='mark_id_mapper', innerjoin=True)
I have data populated in Student Ids, Marks and Subjects table.. Now, I need to populate the junction table with the student_id and the mark foreign keys(Marks obtained in each subject are inserted in the marks table).. How do I get this done, I mean I can create an association proxy inside Student id table that maps with the Mark table and populate it there?
I went through this documentation: http://docs.sqlalchemy.org/en/rel_0_9/orm/extensions/associationproxy.html and I kind of understand that i need this proxy between student_id and marks..so that I can query student_id.mark to obtain marks. But how do i get that in my Junction table?
Can someone please guide me?

I suggest it is one to many relationship (one student - many marks, http://docs.sqlalchemy.org/en/rel_0_9/orm/basic_relationships.html#one-to-many). So you might want to add student_id to your Marks model and relationship with Marks to StudentIds model instead of creating Junction model.
class StudentIds(Base):
__table_args__ = {'extend_existing': True}
__tablename__ = 'student_ids'
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
student_id = Column(String(50, convert_unicode=True), nullable=False,unique=False)
junctionid = relationship("Junction", cascade='save-update',
backref='student_ids', lazy='select', post_update=True)
marks = relationship("Marks", backref="student")
class Marks(Base):
__table_args__ = {'extend_existing': True}
__tablename__ = 'marks'
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
mark = Column(Integer, nullable=False, unique=False)
subject_fk = Column(Integer, ForeignKey('subjects.id'))
subject = relationship('Subjects', foreign_keys=[subject_fk],uselist=False,
backref='marks', innerjoin=False, post_update=False)
student_id = Column(Integer, ForeignKey('student_ids.id'))
student = relationship('StudentIds', foreign_keys=[student_id])
class Subjects(Base):
__table_args__ = {'extend_existing': True}
__tablename__ = 'subjects'
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
subject = Column(String(10, convert_unicode=True), nullable=False, unique=True)
If you want to create many to many relationship, then you might want to look into creating association table (http://docs.sqlalchemy.org/en/rel_0_9/orm/basic_relationships.html#many-to-many)
association_table = Table('association', Base.metadata,
Column('students_id', Integer, ForeignKey('student_ids.id')),
Column('marks_id', Integer, ForeignKey('marks.id'))
)
class Marks(Base):
__table_args__ = {'extend_existing': True}
__tablename__ = 'marks'
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
mark = Column(Integer, nullable=False, unique=False)
subject_fk = Column(Integer, ForeignKey('subjects.id'))
subject = relationship('Subjects', foreign_keys=[subject_fk],uselist=False, backref='marks', innerjoin=False, post_update=False)
class Subjects(Base):
__table_args__ = {'extend_existing': True}
__tablename__ = 'subjects'
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
subject = Column(String(10, convert_unicode=True), nullable=False, unique=True)
class StudentIds(Base):
__table_args__ = {'extend_existing': True}
__tablename__ = 'student_ids'
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
student_id = Column(String(50, convert_unicode=True), nullable=False,unique=False)
junctionid = relationship("Junction", cascade='save-update',
backref='student_ids', lazy='select', post_update=True)
marks = relationship("Marks", secondary=association_table)
Anyways you don't need Junction model for creating relation between students and their marks.

Related

SQLAlchemy ORM - 3-some object relationships

I'm using SQLAlchemy with Postgres.
Code example from https://auth0.com/blog/sqlalchemy-orm-tutorial-for-python-developers/
I have 3 objects:
Actor
Stuntman
ContactDetails
In the original code example, the relationships are as follow:
Actor:ContactDetails = 1:0..*
Actor:Stuntman = 1:1
See Code:
class Actor(Base):
__tablename__ = 'actors'
id = Column(Integer, primary_key=True)
name = Column(String)
birthday = Column(Date)
class Stuntman(Base):
__tablename__ = 'stuntmen'
id = Column(Integer, primary_key=True)
name = Column(String)
active = Column(Boolean)
actor_id = Column(Integer, ForeignKey('actors.id'))
actor = relationship("Actor", backref=backref("stuntman", uselist=False))
class ContactDetails(Base):
__tablename__ = 'contact_details'
id = Column(Integer, primary_key=True)
phone_number = Column(String)
address = Column(String)
actor_id = Column(Integer, ForeignKey('actors.id'))
actor = relationship("Actor")
I'm struggling to extend this model to following relationships:
Actor:ContactDetails = 1:0..*
Actor:Stuntman = 1:1
Stuntman:ContactDetails = 1:0..*
Stuntmen work hard too and deserve ContactDetails. Who can help please?!
In my opinion best way to achieve what you need is using association table. This one should work:
actors_to_contact_details = Table('actors_to_contact_details', Base.metadata,
Column('actor_id', Integer, ForeignKey('actors.id')),
Column('contact_detail_id', Integer, ForeignKey('contact_details.id'))
)
stuntmen_to_contact_details = Table('stuntmen_to_contact_details', Base.metadata,
Column('stuntman_id', Integer, ForeignKey('stuntmen.id')),
Column('contact_detail_id', Integer, ForeignKey('contact_details.id'))
)
class Actor(Base):
__tablename__ = 'actors'
id = Column(Integer, primary_key=True)
name = Column(String)
birthday = Column(Date)
contact_details = relationship("ContactDetails", secondary=actors_to_contact_details)
class Stuntman(Base):
__tablename__ = 'stuntmen'
id = Column(Integer, primary_key=True)
name = Column(String)
active = Column(Boolean)
actor_id = Column(Integer, ForeignKey('actors.id'))
actor = relationship("Actor", backref=backref("stuntman", uselist=False))
contact_details = relationship("ContactDetails", backref="stuntman", secondary=stuntmen_to_contact_details)
class ContactDetails(Base):
__tablename__ = 'contact_details'
id = Column(Integer, primary_key=True)
phone_number = Column(String)
address = Column(String)
if you want, you can put unique=True in association table like this
actors_to_contact_details = Table('actors_to_contact_details', Base.metadata,
Column('actor_id', Integer, ForeignKey('actors.id')),
Column('contact_detail_id', Integer, ForeignKey('contact_details.id'), unique=True)
)
stuntmen_to_contact_details = Table('stuntmen_to_contact_details', Base.metadata,
Column('stuntman_id', Integer, ForeignKey('stuntmen.id')),
Column('contact_detail_id', Integer, ForeignKey('contact_details.id'), unique=True)
)
Other option would be to put another nullable foreign key on Stuntman table, but I would go with solution presented above.

Flask SQLAlchemy Association Table: error creating backref

I have a Artist model, and a Venue model, and they have a many-to-many relationship facilitated by the Show model (the shows indicate the artist, venue and date of the concert).
class Venue(db.Model):
__tablename__ = 'Venue'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, unique=True, nullable=False)
shows = db.relationship('Show', backref='venue', lazy=True)
class Artist(db.Model):
__tablename__ = 'Artist'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, unique=True, nullable=False)
shows = db.relationship('Show', backref='artist', lazy=True)
class Show(db.Model):
__tablename__ = 'Show'
id = db.Column(db.Integer, primary_key=True)
venue_id = db.Column(db.Integer, db.ForeignKey('Venue.id'), nullable=False)
artist_id = db.Column(db.Integer, db.ForeignKey('Artist.id'), nullable=False)
start_time = db.Column(db.DateTime, nullable=False)
venue = db.relationship('Venue', back_populates='venue')
artist = db.relationship('Artist', back_populates='artist')
I'd like to have a query to get the venue name, artist name, and date of all upcoming shows so had in mind the following...
upcoming_shows = Show.query.filter_by(show.start_time >= datetime.now()).all()
...so that I can then access for example, upcoming_shows[0].Venue.name, upcoming_shows[0].Artist.name and upcoming_shows[0].start_time.
That query however returns the following:
sqlalchemy.exc.ArgumentError: Error creating backref 'venue' on
relationship 'Venue.shows': property of that name exists on mapper
'mapped class Show->Show'
Feel like I'm close but missing something fundamental!
Solved. I made the changes below to the code and it works now. It was rather straight-forward in the end, I simply had to ensure that the relationship names were unique :-\
class Venue(db.Model):
__tablename__ = 'Venue'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, unique=True, nullable=False)
venue_shows = db.relationship('Show', back_populates='venue', lazy=True)
class Artist(db.Model):
__tablename__ = 'Artist'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, unique=True, nullable=False)
artist_shows = db.relationship('Show', back_populates='artist', lazy=True)
class Show(db.Model):
__tablename__ = 'Show'
id = db.Column(db.Integer, primary_key=True)
venue_id = db.Column(db.Integer, db.ForeignKey('Venue.id'), nullable=False)
artist_id = db.Column(db.Integer, db.ForeignKey('Artist.id'), nullable=False)
start_time = db.Column(db.DateTime, nullable=False)
venue = db.relationship('Venue', back_populates='venue_shows')
artist = db.relationship('Artist', back_populates='venue_artist')
Here is a general example for what you are looking for
class Association(Base):
__tablename__ = 'association'
left_id = Column(Integer, ForeignKey('left.id'), primary_key=True)
right_id = Column(Integer, ForeignKey('right.id'), primary_key=True)
extra_data = Column(String(50))
child = relationship("Child", back_populates="parents")
parent = relationship("Parent", back_populates="children")
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Association", back_populates="parent")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
parents = relationship("Association", back_populates="child")
For more details check the documentation

How can I attach generic `Contact` to multiple different tables?

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.

SqlAlchemy Error on Creating multiple foreign key to one table

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.

how to insert symbol(,) to result in SQLAlchemy level?

#mapping class
class Billing(Base):
__tablename__ = 'billing'
id = Column(Integer, primary_key=True)
billingdate= Column(DateTime, nullable=False)
amt = Column(Integer, nullable=False)
rate = Column(Integer, nullable=False)
fk_cpid = Column(Integer, ForeignKey('company.cpid'))
#run
query = self.mssql_session.query(Billing.billingdate).all()
result
$83749283 => $83,749,283
how to insert symbol(,) at Billing.billingdate in ONLY SQLAlchemy level?
Replace, SubString?
from sqlalchemy.ext.hybrid import hybrid_property
class Billing(Base):
__tablename__ = 'billing'
id = Column(Integer, primary_key=True)
billingdate= Column(DateTime, nullable=False)
_amt = Column(Integer, nullable=False)
rate = Column(Integer, nullable=False)
fk_cpid = Column(Integer, ForeignKey('company.cpid'))
#hybrid_property
def amt(self):
return '${:,}'.format(self._amt)
Hopes, this code can help you.

Categories