I have proxy association between Content and ContentRevision. Usage of sqlalchemy.ext.associationproxy.association_proxy produce an EXISTS condition:
from sqlalchemy import create_engine, Column, Integer, Text, ForeignKey, inspect, String, and_
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship, foreign, remote
DeclarativeBase = declarative_base()
class ContentRevision(DeclarativeBase):
__tablename__ = 'content_revision'
revision_id = Column(Integer, primary_key=True)
content_id = Column(Integer, ForeignKey('content.id'))
description = Column(Text())
title = Column(String(32))
class Content(DeclarativeBase):
__tablename__ = 'content'
id = Column(Integer, primary_key=True)
revisions = relationship("ContentRevision",
foreign_keys=[ContentRevision.content_id])
revision = relationship(
"ContentRevision",
uselist=False,
primaryjoin=lambda: and_(
remote(Content.id) == foreign(ContentRevision.content_id),
ContentRevision.revision_id == ContentRevision.revision_id == session.query(ContentRevision.revision_id)
.filter(ContentRevision.content_id == Content.id)
.order_by(ContentRevision.revision_id.desc())
.limit(1)
.correlate(Content)
),
)
title = association_proxy('revision', 'title')
description = association_proxy('revision', 'description')
# Prepare database and session
engine = create_engine('sqlite://', echo=False)
DeclarativeBase.metadata.create_all(engine)
session_maker = sessionmaker(engine)
session = session_maker()
#
c1 = Content()
c1.revisions.append(ContentRevision(title='rev', description='rev1'))
session.add(c1)
session.flush()
c1.revisions.append(ContentRevision(title='rev', description='rev2'))
assert [('rev', 'rev1'), ('rev', 'rev2')] == session.query(ContentRevision.title, ContentRevision.description).all()
print(str(session.query(Content).filter(Content.title == 'foo')))
"""
SELECT content.id AS content_id
FROM content
WHERE EXISTS (SELECT 1
FROM content_revision
WHERE content.id = content_revision.content_id AND content_revision.revision_id = (SELECT content_revision.revision_id AS content_revision_revision_id
FROM content_revision
WHERE content_revision.content_id = content.id ORDER BY content_revision.revision_id DESC
LIMIT :param_1) AND content_revision.title = :title_1)
"""
How to make a query on associated column Content.title using the join declared in primaryjoin relationship of Content.revision ?
Related
I'm trying to implement some foreign keys between tables and query using joins as per this ORM tutorial:
from sqlalchemy import (Column, ForeignKey, Integer, create_engine)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
Base = declarative_base()
class DataAccessLayer():
def __init__(self):
conn_string = "mysql+mysqlconnector://root:root#localhost/"
self.engine = create_engine(conn_string)
def create_session(self):
Base.metadata.create_all(self.engine)
Session = sessionmaker()
Session.configure(bind=self.engine)
self.session = Session()
class Bet(Base):
__tablename__ = "bet"
__table_args__ = ({"schema": "belgarath", "extend_existing": True})
id_ = Column(Integer, primary_key=True)
match_id = Column(Integer, ForeignKey("belgarath.match_.id_"))
match_ = relationship("Match", back_populates="belgarath.bet")
class Match(Base):
__tablename__ = "match_"
__table_args__ = ({"schema": "belgarath", "extend_existing": True})
id_ = Column(Integer, primary_key=True)
tournament_id = Column(Integer)= Column(Integer)
dal = DataAccessLayer()
dal.create_session()
bets = dal.session.query(Bet)
bets.join(Match)
for bet in bets:
print(bet.id_, bet.tournament_id = Column(Integer))
However I'm getting the following error on the bets = dal.session.query(Bet) line:
Exception has occurred: InvalidRequestError
Mapper 'mapped class Match->match_' has no property 'belgarath.bet'
Where am I going wrong? Do I need some kind of reciprocal relationship in Match?
When trying to commit a record the following exception arises:
OperationalError (sqlite3.OperationalError) no such
table: basedocument [SQL: INSERT INTO basedocument (common_field,
doc_type) VALUES (?, ?)] [parameters: ('humanidade', 'user')]
basedocument is the parent table in a polimorphic association, and user is the child table.
Shouldn't the parent table be created when I create a record on a child table?
Here's the code:
from sqlalchemy import create_engine, ForeignKey, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.orm import scoped_session, sessionmaker
engine = create_engine("sqlite:///temp.db")
sessao = scoped_session(sessionmaker(bind=engine))
Base = declarative_base()
class BaseDocument(Base):
__tablename__ = "basedocument"
id = Column(Integer, primary_key=True)
common_field = Column(String)
doc_type = Column(String(20))
__mapper_args__ = {
"polymorphic_identity": "basedocument",
"polymorphic_on": doc_type,
}
class User(BaseDocument):
__tablename__ = "user"
id = Column(Integer, ForeignKey("basedocument.id"), primary_key=True)
name = Column(String)
fullname = Column(String)
nickname = Column(String)
__mapper_args__ = {
"polymorphic_identity": "user",
}
u1 = User(
name="Dumont",
fullname="Santos Dumont",
nickname="voador",
common_field="humanidade",
)
sessao.add(u1)
sessao.commit()
print("\nObject: ", u1)
Well, I just noticed that I forgot to use
Base.metadata.create_all(engine)
after the classes definitions. This is what really creates the tables.
This answer is valid for many questions around the "No such table" error.
The right code:
from sqlalchemy import create_engine, ForeignKey, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.orm import scoped_session, sessionmaker
engine = create_engine("sqlite:///temp.db")
sessao = scoped_session(sessionmaker(bind=engine))
Base = declarative_base()
class BaseDocument(Base):
__tablename__ = "basedocument"
id = Column(Integer, primary_key=True)
common_field = Column(String)
doc_type = Column(String(20))
__mapper_args__ = {
"polymorphic_identity": "basedocument",
"polymorphic_on": doc_type,
}
class User(BaseDocument):
__tablename__ = "user"
id = Column(Integer, ForeignKey("basedocument.id"), primary_key=True)
name = Column(String)
fullname = Column(String)
nickname = Column(String)
__mapper_args__ = {
"polymorphic_identity": "user",
}
Base.metadata.create_all(engine)
u1 = User(
name="Dumont",
fullname="Santos Dumont",
nickname="voador",
common_field="humanidade",
)
sessao.add(u1)
sessao.commit()
print("\nObject: ", u1)
lets say I've 2 tables users and devices. They have relation one-to-many.
In Sql, I can solve the mentioned problem by following query.
SELECT
users.*, devices.*
FROM
users
LEFT JOIN ( SELECT d1.*
FROM devices as d1
LEFT JOIN devices AS d2
ON d1.user_id = d2.user_id AND d1.date < d2.date
WHERE d2.user_id IS NULL ) as device_temp
ON (users.id = device_temp.user_id)
Here is my python code
#user_model.py
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
first_name = Column(String(500), nullable=False)
last_name = Column(String(250), nullable=False)
device_model.py
#device_model.py
from sqlalchemy import Column, ForeignKey, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, relation
from sqlalchemy import create_engine
from user_model import User
Base = declarative_base()
class DeviceModel(Base):
__tablename__ = 'device'
id = Column(Integer, primary_key=True)
created_at = Column(DateTime(), nullable=False)
device_id = Column(String(250), nullable=False)
user_uid = Column(String, ForeignKey((User.id)))
owner = relation(User, backref='user_device')
run.py
#run.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from user_model import User, Base
from sleep_session import SleepSession, Base
from device_model import DeviceModel, Base
engine = create_engine(connection_string)
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()
query = session.query(User,DeviceModel).join(DeviceModel)
results = query.all()
for row in results:
print(row.User.first_name +" "+ row.DeviceModel.device_id + " "+ str(row.DeviceModel.created_at))
I know this type of question is asked multiple times, but I could not find one with SqlAlchemy ORM.
I want the same result as described here
Thanks.
I used this question to practive sqlalchemy as I'm new to it.
Closest answer I can get is the following:
If you want to see 1 file full workable code go into the edits - I'll remove boilerplate code
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy import Column, ForeignKey, Integer, String, DateTime
from sqlalchemy.orm import relationship, relation
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
connection_string = 'postgres://postgres:password#localhost/test'
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
first_name = Column(String(500), nullable=False)
last_name = Column(String(250), nullable=False)
class DeviceModel(Base):
__tablename__ = 'device'
id = Column(Integer, primary_key=True)
created_at = Column(DateTime(), nullable=False)
device_id = Column(String(250), nullable=False)
user_uid = Column(Integer, ForeignKey((User.id))) # error Key columns "user_uid" and "id" are of incompatible types: character varying and integer.
owner = relation(User, backref='user_device')
engine = create_engine(connection_string)
Base.metadata.bind = engine
#User.__table__.create(engine)
#DeviceModel.__table__.create(engine)
DBSession = sessionmaker(bind=engine)
session = DBSession()
My Answer:
from sqlalchemy import and_, or_
from sqlalchemy.orm import aliased
DeviceModel2 = aliased(DeviceModel)
subquery = (session
.query(DeviceModel.created_at)
.outerjoin(DeviceModel2,
and_(DeviceModel.user_uid == DeviceModel2.user_uid,
DeviceModel.created_at < DeviceModel2.created_at))
.filter(DeviceModel2.user_uid == None)
.subquery('subq'))
query = (session
.query(User, DeviceModel)
.outerjoin(DeviceModel)
.filter(or_(
DeviceModel.created_at == subquery.c.created_at,
DeviceModel.id == None)))
print(query)
results = query.all()
for row in results:
if row[1]:
print({**row.User.__dict__, **row.DeviceModel.__dict__})
else:
print(row.User.__dict__)
from db_config import connection_string
from sqlalchemy import create_engine , and_ , inspect
from sqlalchemy.orm import sessionmaker, aliased
from user_model import User, Base
from device_model import DeviceModel, Base
engine = create_engine(connection_string)
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()
DeviceModel_aliased = aliased(DeviceModel)
#make sub-query
query_for_latest_device = session.query(DeviceModel).\
outerjoin(DeviceModel_aliased,
and_(DeviceModel_aliased.user_uid == DeviceModel.user_uid,
DeviceModel_aliased.created_at > DeviceModel.created_at)).\
filter(DeviceModel_aliased.id == None).\
subquery()
use_subquery_and_join = session.query(User.first_name,latest_device).\
join(query_for_latest_device,
query_for_latest_device.c.user_uid == User.user_id).\
all()
for row in join_user_and_device:
print(row._asdict())
How do i extract the table name from SQLAlchemy exist statement
assume we have the following code
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class Person(Base):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
name = Column(String(250), nullable=False)
engine = create_engine('mysql://...')
Session = sessionmaker(bind=engine)
conn = engine.connect()
session = Session(bind=conn)
query_exists = session.query(Person).exists()
how can i extract the table name from the query_exists?
from sqlalchemy.sql.visitors import ClauseVisitor
from sqlalchemy import Table
def extract_tables(sql_stmt):
tables = []
visitor = ClauseVisitor()
cluase_iter = visitor.iterate(elem)
for e in cluase_iter:
if isinstance(e, Table):
tables.append(e)
if isinstance(e, (ValuesBase, UpdateBase)):
tables.append(e.table)
return set(tables)
Could some one help me figure out how should i write primaryjoin/secondaryjoin
on secondary table that lacking one ForeignKey definition. I can't modify database
itself since it's used by different application.
from sqlalchemy import schema, types, func, orm
from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class A(Base):
__tablename__ = 'atab'
id = schema.Column(types.SmallInteger, primary_key=True)
class B(Base):
__tablename__ = 'btab'
id = schema.Column(types.SmallInteger, primary_key=True)
a = orm.relationship(
'A', secondary='abtab', backref=orm.backref('b')
)
class AB(Base):
__tablename__ = 'abtab'
id = schema.Column(types.SmallInteger, primary_key=True)
a_id = schema.Column(types.SmallInteger, schema.ForeignKey('atab.id'))
b_id = schema.Column(types.SmallInteger)
I've tried specifing foreign on join condition:
a = orm.relationship(
'A', secondary='abtab', backref=orm.backref('b'),
primaryjoin=(id==orm.foreign(AB.b_id))
)
But received following error:
ArgumentError: Could not locate any simple equality expressions involving locally mapped foreign key columns for primary join condition '"atab".id = "abtab"."a_id"' on relationship Category.projects. Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or are annotated in the join condition with the foreign() annotation. To allow comparison operators other than '==', the relationship can be marked as viewonly=True.
You can add foreign_keys to your relationship configuration. They mention this in a mailing list post:
from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
logon = Column(String(10), primary_key=True)
group_id = Column(Integer)
class Group(Base):
__tablename__ = 'groups'
group_id = Column(Integer, primary_key=True)
users = relationship('User', backref='group',
primaryjoin='User.group_id==Group.group_id',
foreign_keys='User.group_id')
engine = create_engine('sqlite:///:memory:', echo=True)
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
u1 = User(logon='foo')
u2 = User(logon='bar')
g = Group()
g.users = [u1, u2]
session.add(g)
session.commit()
g = session.query(Group).first()
print([user.logon for user in g.users])
output:
['foo', 'bar']