Sqlalchemy and many to many relationship linking two tables - python

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

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

Flask-SQLAlchemy: User role Models and relationship

I am trying to create a Flask-SqlAlchemy database model and need users to be linked to a single role. Example roles: "User", "Admin", "Moderator".
Surely I could use a simple relatioship, which just adds a user_role_id Column to the User, but I would also like to track the last modification.
class User:
id = db.Column(db.Integer, primary_key=True)
class UserRole:
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False)
class UserRoleLink(db.Model):
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), primary_key=True)
user_role_id = db.Column(db.Integer, db.ForeignKey("user_role.id"), primary_key=True)
last_modified = db.Column(db.DateTime, nullable=False, default=datetime.now, onupdate=datetime.now)
How do I complete this model with the required relationships? Also I would like to ensure, that a User can only have one Role, but Roles can be used as often as required.
Thank you in advance!
I think it should works for you:
class User:
id = db.Column(db.Integer, primary_key=True)
class UserRole:
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False, unique=True)
role_link = db.relationship('UserRoleLink', cascade='all, delete-orphan', uselist=False)
class UserRoleLink(db.Model):
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), primary_key=True)
user_role_id = db.Column(db.Integer, db.ForeignKey("user_role.id"), primary_key=True)
last_modified = db.Column(db.DateTime, nullable=False, default=datetime.now, onupdate=datetime.now)

in Sqlalchemy can I add a column with "source table" name in union?

I have these two tables - sales and purchases. And then "stock" which is related to them. This is Flask-Sqlalchemy by the way, but I don't know if that's important.
class Sales(db.Model):
__tablename__ = "sales"
id = db.Column(db.Integer, primary_key=True)
stock_id = db.Column(db.Integer, db.ForeignKey(
'stocks.id'), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
amount = db.Column(db.Integer, nullable=False, default=1)
unit_price = db.Column(db.Numeric, nullable=False)
total_price = db.Column(db.Numeric, nullable=False)
time = db.Column(db.DateTime, nullable=False,
default=datetime.datetime.utcnow())
class Purchases(db.Model):
__tablename__ = "purchases"
id = db.Column(db.Integer, primary_key=True)
stock_id = db.Column(db.Integer, db.ForeignKey(
'stocks.id'), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
amount = db.Column(db.Integer, nullable=False, default=1)
unit_price = db.Column(db.Numeric, nullable=False)
total_price = db.Column(db.Numeric, nullable=False)
time = db.Column(db.DateTime, nullable=False,
default=datetime.datetime.utcnow())
class Stock(db.Model):
__tablename__ = "stocks"
id = db.Column(db.Integer, primary_key=True)
symbol = db.Column(db.String(), nullable=False, unique=True)
name = db.Column(db.String, nullable=False)
I need to make an overview of the complete transactions (and then join it with the Stock table to be able to see the name of the product being sold/bought)
These two tables are identical. The only difference is that one is sales and one is purchases. And I need to make this clear in the data resulting from the query - which rows are from "sales" and which are from "purchases"
Would it have been smarter to make one table instead with all transactions and a column for specifying whether it was a sale or a purchase from the beginning? Or using positive and negative numbers for "price total"?
Can I make a union of the two tables but somehow also specify what rows came from what table? Like adding an extra column where the content is "0" if it's a sale and "1" if it's a purchase?
If someone would show me some example queries including how to also make the join to include "stock symbol" or "stock name" coming from the Stock table I'd be most grateful.
You can use the label function in conjunction with the text function to create a 'source' column.
from sqlalchemy.sql import text
q1 = db.session.\
query(
text("Purchases").label("SourceTable"),
Purchases.stock_id.label("PurchaseStockId")
Stock.id.label("StockId")).\
join(
Purchases,
Purchases.stock_id == Stock.id)
q2= db.session.\
query(
text("Sales").label("SourceTable"),
Sales.stock_id.label("SalesStockId"),
Stock.id.label("StockId")).\
join(
Sales,
Sales.stock_id == Stock.id)
q3 = q1.union(q2).all()

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)

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