SQLAlchemy ORM: Mapping a non-unique column to schema - python

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'),
)

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 - two foreign keys and bidirectional relationship

I have a small base for a website for a real estate agency, below are two tables:
class Person(Base):
__tablename__ = "people"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, nullable=False)
surname = Column(String, nullable=False)
city = Column(String, nullable=True)
# TODO - add lists
class Property(Base):
__tablename__ = "properties"
id = Column(Integer, primary_key=True, index=True)
city = Column(String, nullable=False)
address = Column(String, nullable=False)
owner_id = Column(Integer, ForeignKey("people.id"), nullable=False)
manager_id = Column(Integer, ForeignKey("people.id"), nullable=False)
# TODO - rework
owner = relationship("Person", foreign_keys=[owner_id], backref=backref("owners"))
manager = relationship("Person", foreign_keys=[manager_id], backref=backref("managers"))
I would like my 'Person' object to have two lists of properties - "owned_properites" and "properties_to_manage" (without losing reference to the owner/manager in the 'Property' class). But i don't know how to define a relationship to make auto mapping work properly.
If the class 'Property' only had one foreign key to the 'Person', for example - only "owner_id" key and "owner" object then it would be simple:
#in Property
owner_id = Column(Integer, ForeignKey("people.id"), nullable=False)
owner = relationship("Person", back_populates="property")
#in Person
owned_properties = relationship("Property", back_populates="owner")
But how to do the same with two keys, as shown at the beginning?

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: joined tables and one result object

I am coming from a python-django and am trying to get a grasp on flask-SQLAlchemy:
class Author(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(128), nullable=False)
class Book(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(128), nullable=False)
author = db.Column(db.Integer, db.ForeignKey('author.id'), nullable=False)
I want to get a joined result list:
results = Book.query.filter(Author.name=='tom')
for result in results:
print(result.title, result.???.name)
How do I access the fields of the joined tables?
I figured it out:
class Book(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(128), nullable=False)
author_id = db.Column(db.Integer, db.ForeignKey('author.id'), nullable=False)
author = relationship("Author")
I needed to add the line
author = relationship("Author")
to the model. It seems to be necessary to declare the relationship on the object level. I did miss this.
Now the line can be:
print(result.title, result.author.name)

Order results by link defined in 'relationship'

I have tables that I want to do an ORM based query.
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
is_active = Column(Boolean, server_default="1", nullable=False)
is_deleted = Column(Boolean, server_default="0", nullable=False)
created_at = Column(DateTime, nullable = False, default=func.now())
first_name = Column(String(30), nullable=False)
surname_name = Column(String(30), nullable=False)
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
is_active = Column(Boolean, server_default="1", nullable=False)
is_deleted = Column(Boolean, server_default="0", nullable=False)
created_at = Column(DateTime, nullable = False, default=func.now())
first_name = Column(String(30), nullable=False)
surname_name = Column(String(30), nullable=False)
appointments = relationship("ChildAppointment", backref="child")
class ParentChild(Base):
'''provides a many to many relationship for parent and child'''
__tablename__ = 'parent_child'
id = Column(Integer, primary_key=True)
is_active = Column(Boolean, nullable=False, server_default="1")
is_deleted = Column(Boolean, nullable=False, server_default="0")
parent_id = Column(Integer, ForeignKey('parent.id'), nullable=False)
child_id = Column(Integer, ForeignKey('child.id'))
parents = relationship("Parent", backref="parent_child")
children = relationship("Child", backref="parent_child")
class ChildAppointment(Base):
__tablename__ = 'child_appointment'
id = Column(Integer, primary_key=True)
is_active = Column(Boolean, nullable=False, server_default="1")
is_deleted = Column(Boolean, nullable=False, server_default="0")
created_at = Column(DateTime, nullable = False, default=func.now())
child_id = Column(Integer, ForeignKey('child.id'))
date_appointment = Column(DateTime, nullable = False)
I want to query Table ParentChild and through SQLAlchemy relationship magic, I want to get the latest appointment date for the child meaning I need the result ordered by date_appointment in the child table.
I have followed the example by #zeek here and I've come up with the following:
for data in session.query(ParentChild).join(Child.appointments).filter(ParentChild.parent_id == 1).\
order_by(Child.appointments.date_appointment.desc()).all():
However, I get the error attributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with Child.appointments has an attribute 'date_appointment'
My scenario breaks what was given in that link because mine goes an extra table in as I have 4 tables so am unable to adapt his answer to suit mine.
Thanks.
Two things: First of all, for the join condition, you would want to specify it as a kind of chain:
ParentChild -> Child
Child -> ChildAppointment
That is done like this:
session.query(ParentChild).join(ParentChild.children).join(Child.appointments)
This will yield the correct join. I think if it is unambigous you could also do this:
session.query(ParentChild).join(Child).join(ChildAppointment)
But you'd have to try it out.
The real error, however, comes from this part:
Child.appointments.date_appointment
Instead it should look like this:
ChildAppointment.date_appointment
SQLAlchemy already knows how to get this ChildAppointment related to Child and ParentChild because you specified it in the joins above. And if you think about it in raw SQL terms it makes much more sense this way.
So you final solution:
session.query(ParentChild).join(ParentChild.children).join(Child.appointments).filter(ParentChild.parent_id == 1).order_by(ChildAppointment.date_appointment.desc()).all():

Categories

Resources