FULL JOIN in SQLAlchemy? - python

I would like to display a list of "last entries" in a budget app. The entries (like expenses, income, account transfers, loans) have different columns defined apart from a user_id.
In SQL I would go for a FULL JOIN, but I am using SQLAlchemy (declarative). What is the correct approach here? Some meta table?
Thanks a lot.
Example tables:
class Expense(Base):
__tablename__ = 'expenses'
id = Column(Integer, primary_key=True)
user = Column('user_id', Integer, ForeignKey('users.id'))
date = Column(Integer)
category = Column('category_id', Integer, ForeignKey('expense_categories.id'))
description = Column(String(50))
deduct_from = Column('account_id', Integer, ForeignKey('accounts.id'))
amount = Column(Float(precision=2))
class Loan(Base):
__tablename__ = 'loans'
id = Column(Integer, primary_key=True)
from_user = Column('from_user_id', Integer, ForeignKey('users.id'))
to_user = Column('to_user_id', Integer, ForeignKey('users.id'))
date = Column(Integer)
account = Column('account_id', Integer, ForeignKey('accounts.id'))
description = Column(String(50))
amount = Column(Float(precision=2)

You'll have to use raw SQL if your database supports it or an union otherewise. from http://groups.google.com/group/sqlalchemy/msg/80ea8e712380bff4

Related

Correlated subquery in SQLAlchemy

I have the following query:
SELECT ID, (SELECT SUM(AMOUNT) FROM PURCHASE_PAYMENTS WHERE PURCHASE_PAYMENTS.PURCHASE_ID
= PURCHASES.ID) AS PAID
-> FROM PURCHASES;
which works as I expect. I have problems translating this to SQLAlchemy. My mapped classes are ( I only show the fields of interest):
class Purchase(Base):
__tablename__ = 'purchases'
id = Column(Integer, primary_key=True)
class PurchasePayment(Base):
__tablename__ = 'purchase_payments'
id = Column(Integer, primary_key=True)
purchase_id = Column(Integer, ForeignKey('purchases.id'))
amount = Column(Numeric(19, 4), nullable=False)
Also, I need the results to be like,
(models.db.Purchase object at 0x0000021DD3683D..., Decimal('1234'))
I tried unsuccessfully to follow this post Can we make correlated queries with SQLAlchemy.
Any help?

Flask SQLAlchemy: adding third column to joining table

Context: I'm making an auctioning website for which I am using Flask-SQLAlchemy. My tables will need to have a many-to-many relationship (as one artpiece can have many user bids and a user can bid on many artpieces)
My question is: it is possible to add another column to my joining table to contain the id of the user bidding, the id of artpiece that they are bidding on and also how much they bid? Also if yes, how would I include this bid in the table when I add a record to said table?
bid_table = db.Table("bid_table",
db.Column("user_id", db.Integer, db.ForeignKey("user.user_id")),
db.Column("item_id", db.Integer, db.ForeignKey("artpiece.item_id"))
)
class User(db.Model):
user_id = db.Column(db.Integer, unique=True, primary_key=True, nullable=False)
username = db.Column(db.Integer, unique=True, nullable=False)
email = db.Column(db.String(50), unique =True, nullable=False)
password = db.Column(db.String(60), nullable=False)
creation_date = db.Column(db.DateTime, default=str(datetime.datetime.now()))
bids = db.relationship("Artpiece", secondary=bid_table, backref=db.backref("bids", lazy="dynamic"))
class Artpiece(db.Model):
item_id = db.Column(db.Integer, unique=True, primary_key=True, nullable=False)
artist = db.Column(db.String(40), nullable=False)
buyer = db.Column(db.String(40), nullable=False)
end_date = db.Column(db.String(40))
highest_bid = db.Column(db.String(40))
It is possible to do this with SQL Alchemy, but it's very cumbersome in my opinion.
SQLAlchemy uses a concept called an Association Proxy to turn a normal table into an association table. This table can have whatever data fields you want on it, but you have to manually tell SQLAlchemy which columns are foreign keys to the other two tables in question.
This is a good example from the documentation.
In your case, the UserKeyword table is the association proxy table that you want to build for your user/bid scenario.
The special_key column is the arbitrary data you would store like the bid amount.
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import backref, declarative_base, relationship
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String(64))
# association proxy of "user_keywords" collection
# to "keyword" attribute
keywords = association_proxy('user_keywords', 'keyword')
def __init__(self, name):
self.name = name
class UserKeyword(Base):
__tablename__ = 'user_keyword'
user_id = Column(Integer, ForeignKey('user.id'), primary_key=True)
keyword_id = Column(Integer, ForeignKey('keyword.id'), primary_key=True)
special_key = Column(String(50))
# bidirectional attribute/collection of "user"/"user_keywords"
user = relationship(User,
backref=backref("user_keywords",
cascade="all, delete-orphan")
)
# reference to the "Keyword" object
keyword = relationship("Keyword")
def __init__(self, keyword=None, user=None, special_key=None):
self.user = user
self.keyword = keyword
self.special_key = special_key
class Keyword(Base):
__tablename__ = 'keyword'
id = Column(Integer, primary_key=True)
keyword = Column('keyword', String(64))
def __init__(self, keyword):
self.keyword = keyword
def __repr__(self):
return 'Keyword(%s)' % repr(self.keyword)
Check out the full documentation for instructions on how to access and create this kind of model.
Having used this in a real project, it's not particularly fun and if you can avoid it, I would recommend it.
https://docs.sqlalchemy.org/en/14/orm/extensions/associationproxy.html

SQLAlchemy - Complex Sub-querying with join

I need to be able to convert this to SQLALchemy, I'm a bit new but it is required. The SQL Query is this:
SELECT name,
(
SELECT value from account_settings
WHERE name = "max_allowed_records" and accounts.id = account_settings.account_id
) AS max_allowed_records,
(
SELECT count(*) from employees
join employeelists on employees.employeelist_id = employeelists.id
where employeelists.account_id = accounts.id
group by employeelists.account_id
) AS RECORD_COUNT FROM accounts
having coalesce(max_allowed_records,0) < coalesce(RECORD_COUNT,0);
In Joined form, I think they produce the same result (tried it like this to better visualize it in SQLAlchemy Query):
SELECT accounts.name, account_settings.value AS max_allowed_records, count(employees.id) as RECORD_COUNT
FROM accounts
left join account_settings on account_settings.account_id = accounts.id and account_settings.name = "max_allowed_records"
left join employeelists on employeelists.account_id = accounts.id
left join employees on employees.employeelist_id = employeelists.id
group by accounts.id
having coalesce(account_settings.value,0) < coalesce(RECORD_COUNT,0)
So what I want here is that I should get the value from account settings of the account where in the name is max_allowed_records then compare that to how many employees that account has. Unfortunately the only link between account and the employees is the employeelist. I was wondering if you guys could guide me on what I need to do here? SQLAlchemy joins? or sub queries?
Table Definitions:
class Account(Base):
__tablename__ = "accounts"
id = synonym("raw_id")
name = Column(String(255), nullable=False)
settings = relationship("AccountSetting")
class AccountSetting(Base):
__tablename__ = "account_settings"
id = Column(Integer, primary_key=True)
account_id = Column(Integer, ForeignKey("accounts.id"))
name = Column(String(30))
value = Column(String(1000))
class EmployeeList(Base):
__tablename__ = "employeelists"
id = Column(Integer, primary_key=True)
raw_id = Column("id", Integer, primary_key=True)
name = Column(String(255))
account_id = Column(Integer, ForeignKey("accounts.id"))
class Employee(Base):
__tablename__ = "employees"
id = synonym("raw_id")
raw_id = Column("id", Integer, primary_key=True)
employeelist_id = Column(Integer, ForeignKey("employeelists.id"), nullable=False)
After the whole weekend resting my mind, I was able to convert the SQL Query to SQLAlchemy Query:
query = query.outerjoin(max_records_settings,\
and_(max_records_settings.name == "max_allowed_records",\
Account.id == max_records_settings.account_id))\
.outerjoin(EmployeeList, Account.id == EmployeeList.account_id)\
.add_columns(max_records_settings.value)\
.join(Employee, EmployeeList.id == Employee.employeelist_id)\
.group_by(Account.id)\
.having(coalesce(func.count(Employee.id),0) > (coalesce(max_records_settings.value,0)))\

Flask SQLAlchemy: Child table with multiple parents?

I'm new to flask_sqlalchemy, and while I understand how the one-to-many, and many-to-many relationships work, I'm struggling to understand how I can apply them to my specific data types. I have the following three tables: TeamStat, PlayerStat, and Stat which are loosely described as follows
class PlayerStat(db.Model):
id = db.Column(db.Integer, primary_key=True)
date = db.Column(db.DateTime, nullable=False, default=datetime.datetime)
player_id = db.Column(db.Integer, db.ForeignKey('player.player_id'), nullable=False)
class TeamStat(db.Model):
id = db.Column(db.Integer, primary_key=True)
date = db.Column(db.DateTime, nullable=False, default=datetime.datetime)
team_id = db.Column(db.Integer, db.ForeignKey('team.team_id'), nullable=False)
class Stat(db.Model):
id = db.Column(db.Integer, primary_key=True)
value = db.Column(db.String(80), nullable=False)
Since Stat is a generic table type, I would like to use it by both the PlayerStat table (for individual player stats), as well as the TeamStat table (as a sum of the stats for all players on a team). Can someone help me understand how I can refer one child table to multiple parent tables in this fashion?
Use association tables to bridge your stat table to your players and team tables. This example is pretty close to what you are already doing, except the date column is moved on to the stat record table and I've replaced your PlayerStat and TeamStat objects with unmapped tables.
I've assumed you have two ORM classes, Player and Team (not Flask-SQLAlchemy sorry but concept remains the same):
plr_stat_assc = Table('plr_stat_assc', Base.metadata,
Column('player_id', Integer, ForeignKey('player.id')),
Column('stat_id', Integer, ForeignKey('stat.id'))
)
team_stat_assc = Table('team_stat_assc', Base.metadata,
Column('team_id', Integer, ForeignKey('team.id')),
Column('stat_id', Integer, ForeignKey('stat.id'))
)
class Player(Base):
__tablename__ = 'player'
id = Column(Integer, primary_key=True)
stats = relationship("Stat", secondary=plr_stat_assc)
class Team(Base):
__tablename__ = 'team'
id = Column(Integer, primary_key=True)
stats = relationship("Stat", secondary=team_stat_assc)
class Stat(Base):
__tablename__ = 'stat'
id = Column(Integer, primary_key=True)
date = Column(DateTime, nullable=False, default=datetime.datetime)
value = Column(String(80), nullable=False)

Generated queries contain redundant products?

I have rather simple models like these:
TableA2TableB = Table('TableA2TableB', Base.metadata,
Column('tablea_id', BigInteger, ForeignKey('TableA.id')),
Column('tableb_id', Integer, ForeignKey('TableB.id')))
class TableA(Base):
__tablename__ = 'TableA'
id = Column(BigInteger, primary_key=True)
infohash = Column(String, unique=True)
url = Column(String)
tablebs = relationship('TableB', secondary=TableA2TableB, backref='tableas')
class TableB(Base):
__tablename__ = 'TableB'
id = Column(Integer, primary_key=True)
url = Column(String, unique=True)
However, sqla generates queries like
SELECT "TableB".id, "TableB".url AS "TableB_url" FROM "TableB", "TableA2TableB"
WHERE "TableA2TableB".tableb_id = "TableB".id AND "TableA2TableB".tablea_id = 408997;
But why is there a cartesian product in the query when the attributes selected are those in TableB? TableA2TableB shouldn't be needed.
Thanks
As it is right now, there is a backref relationship in TableB (tableas) and it's loaded because the default loading mode is set to select.
You may want to change the TableA.tablebs to
tablebs = relationship('TableB', secondary=TableA2TableB, backref='tableas', lazy="dynamic")

Categories

Resources