'Dynamically assigning multiple relationship on a model in SQLAlchemy
I am trying to generate dynamic sql queries based on certain conditions in SQLAlchemy. I have a model that is gets queried constantly based on different conditions
The code goes like this:
Models:
from sqlalchemy import Column, ForeignKey, String, create_engine, INTEGER
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from database import db_url
engine = create_engine(db_url, echo=True)
session_maker = sessionmaker(bind=engine)
session = session_maker()
Base = declarative_base()
class Fund(Base):
__tablename__ = 'fund'
port_id = Column(String(4), primary_key=True, nullable=False)
@property
def serialize(self):
attrs = list(map(lambda x: x.serialize, self.attributes))
return {
'portId': self.port_id,
'attributes': attrs
}
class FundAttribute(Base):
__tablename__ = 'attribute'
id = Column(INTEGER, primary_key=True)
fund_port_id = Column(String(4), ForeignKey('fund.port_id'))
attribute_name = Column(String)
@property
def serialize(self):
return {
'attributeName': self.attribute_name
}
main.py
from flask import Flask, jsonify
from sqlalchemy.orm import relationship
from models import Fund, session
app = Flask(__name__)
funds = ['0001', '0002', '0003']
attributes_1 = [
'Short Name',
'Full Name'
]
attributes_2 = [
'Retirement Year',
'Currency'
]
flag = False
@app.route('/')
def get_funds():
global flag
flag = not flag
print('flag===', flag)
join_condition = ''
if flag:
join_condition = f'and_(Fund.port_id==FundAttribute.fund_port_id, FundAttribute.attribute_name.in_({attributes_1}))'
Fund.attributes = relationship('FundAttribute', primaryjoin=join_condition, lazy='selectin')
else:
join_condition = f'and_(Fund.port_id==FundAttribute.fund_port_id, FundAttribute.attribute_name.in_({attributes_2}))'
Fund.attributes = relationship('FundAttribute', primaryjoin=join_condition, lazy='selectin')
print('join_condition', join_condition)
res = session.query(Fund).filter(Fund.port_id.in_(funds)).all()
res = list(map(lambda x: x.serialize, res))
print(res)
return jsonify(res)
if __name__ == '__main__':
app.run(port=8081)
I have setup the code in such a way that it runs both conditions alternately. When I run the code for the first time, it creates a join condition for attributes_1 Here's the log:
flag=== True
join_condition == and_(Fund.port_id==FundAttribute.fund_port_id, FundAttribute.attribute_name.in_(['Short Name', 'Full Name']))
SELECT mv_funds_common.port_id AS mv_funds_common_port_id FROM mv_funds_common WHERE mv_funds_common.port_id IN (%(port_id_1_1)s, %(port_id_1_2)s, %(port_id_1_3)s)
[generated in 0.00045s] {'port_id_1_1': '0001', 'port_id_1_2': '0002', 'port_id_1_3': '0003'}
SELECT mv_funds_common_1.port_id AS mv_funds_common_1_port_id, mv_fund_attribute.id AS mv_fund_attribute_id, mv_fund_attribute.fund_port_id AS mv_fund_attribute_fund_port_id, mv_fund_attribute.attribute_name AS mv_fund_attribute_attribute_name FROM mv_funds_common AS mv_funds_common_1 JOIN mv_fund_attribute ON mv_funds_common_1.port_id = mv_fund_attribute.fund_port_id AND mv_fund_attribute.attribute_name IN (%(attribute_name_1_1)s, %(attribute_name_1_2)s) WHERE mv_funds_common_1.port_id IN (%(primary_keys_1)s, %(primary_keys_2)s)
[generated in 0.00022s] {'attribute_name_1_1': 'Short Name', 'attribute_name_1_2': 'Full Name', 'primary_keys_1': '0001', 'primary_keys_2': '0002'}
In theory. when I run the code again, it should run the else part, i.e for attributes_2. Code enters the else part, and creates the join_condition with attributes_2:
I verified that through the print statements:
flag=== False
join_condition == and_(Fund.port_id==FundAttribute.fund_port_id, FundAttribute.attribute_name.in_(['Retirement Year', 'Currency']))
However, that doesn't get assigned as relationship. It only prints attributes_1 in the query when SQLAlchemy creates the relationship.
What caught my attention was [cached since 6.906s ago] (Check printed logs and generated sql statements below). Does SQLAlchemy caches the query, if yes, is there a way to disable this cache. May be the relationship is not getting generated dynamically.
SELECT mv_funds_common.port_id AS mv_funds_common_port_id FROM mv_funds_common WHERE mv_funds_common.port_id IN (%(port_id_1_1)s, %(port_id_1_2)s, %(port_id_1_3)s)
[cached since 6.917s ago] {'port_id_1_1': '0001', 'port_id_1_2': '0002', 'port_id_1_3': '0003'} main.py:37: SAWarning: Property Fund.attributes on mapped class Fund->mv_funds_common being replaced with new property Fund.attributes; the old property will be discarded Fund.attributes = relationship('FundAttribute', primaryjoin=join_condition, lazy='selectin')
SELECT mv_funds_common_1.port_id AS mv_funds_common_1_port_id, mv_fund_attribute.id AS mv_fund_attribute_id, mv_fund_attribute.fund_port_id AS mv_fund_attribute_fund_port_id, mv_fund_attribute.attribute_name AS mv_fund_attribute_attribute_name FROM mv_funds_common AS mv_funds_common_1 JOIN mv_fund_attribute ON mv_funds_common_1.port_id = mv_fund_attribute.fund_port_id AND mv_fund_attribute.attribute_name IN (%(attribute_name_1_1)s, %(attribute_name_1_2)s) WHERE mv_funds_common_1.port_id IN (%(primary_keys_1)s, %(primary_keys_2)s)
[cached since 6.906s ago] {'attribute_name_1_1': 'Short Name', 'attribute_name_1_2': 'Full Name', 'primary_keys_1': '0001', 'primary_keys_2': '0002'} [{'portId': '0001', 'attributes': []}, {'portId': '0002', 'attributes': []}]
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
