我正在尝试将SQLAlchemy中的两个历史表(主从细节)连接起来。
每个表都有一个开始和结束日期,并且两个具有相同ID的记录是同一对象的不同版本。当查询当前对象的引用对象时,我想接收最新版本。
我无法建立关系来查询主对象的详细信息。
这里是一个例子:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from datetime import date
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, remote, foreign
from sqlalchemy import Column, Integer, String, Date, ForeignKey, cast, and_
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
__all__ = ["Base", "Street", "Place"]
Base = declarative_base()
class Street(Base):
__tablename__ = 'streets'
id = Column(Integer, primary_key=True)
start_date = Column(Date, primary_key=True)
end_date = Column(Date, default=date(9999, 12, 31), nullable=False)
name = Column(String(50), nullable=False)
def __repr__(self) -> str:
return "<Street(id={}, name='{}')>".format(self.id, self.name)
def __init__(self, id: int, name: str):
self.id = id
self.start_date = date.today()
self.name = name
class Place(Base):
__tablename__ = 'places'
id = Column(Integer, primary_key=True)
start_date = Column(Date, primary_key=True)
end_date = Column(Date, default=date(9999, 12, 31), nullable=False)
name = Column(String(50), nullable=False)
# street_id = Column(Integer, ForeignKey("Street.id"), nullable=False)
street_id = Column(Integer, nullable=False)
def __repr__(self) -> str:
return "<Place(id={}, name='{}', street_id={})>".format(self.id, self.name, self.street_id)
def __init__(self, id: int, name: str, street_id: str):
self.id = id
self.start_date = date.today()
self.name = name
self.street_id = street_id
Place.street = relationship(Street,
primaryjoin=and_(remote(Street.id) == foreign(Place.street_id),
cast(remote(Street.end_date), Date) == "9999-12-31"))
Street.places = relationship(Place, order_by=Place.id,
primaryjoin=and_(remote(Street.id) == foreign(Place.street_id),
# cast(remote(Place.end_date), Date) == "9999-12-31"))
cast(Place.end_date, Date) == "9999-12-31"))
# cast(foreign(Place.end_date), Date) == "9999-12-31"))
if __name__ == "__main__":
engine = create_engine("sqlite://") # , echo=True)
Session = sessionmaker(bind=engine)
session = Session()
Base.metadata.create_all(engine)
session.query(Place).delete()
session.query(Street).delete()
session.commit()
session.expunge_all()
session.add_all([Street(1, "First Av."), Street(2, "Second Av.")])
session.add_all([Place(11, "Place 1, First Av.", 1), Place(12, "Place 2, First Av.", 1),
Place(21, "Place 1, Second Av.", 2), Place(22, "Place 2, Second Av.", 2)])
session.commit()
session.expunge_all()
ps = session.query(Place).all()
for p in ps:
print(str(p) + str(p.street))
ss = session.query(Street).all()
for s in ss:
print(str(s))
for p in s.places:
print(" " + str(p))
在此代码中,您可以看到详细信息可以引用它自己的母版,但是母版可以引用所有详细信息,而不是它自己的。
这种关系有什么问题?
结果是:
<Street(id=1, name='First Av.')>
<Place(id=11, name='Place 1, First Av.', street_id=1)>
<Place(id=12, name='Place 2, First Av.', street_id=1)>
<Place(id=21, name='Place 1, Second Av.', street_id=2)>
<Place(id=22, name='Place 2, Second Av.', street_id=2)>
<Street(id=2, name='Second Av.')>
<Place(id=11, name='Place 1, First Av.', street_id=1)>
<Place(id=12, name='Place 2, First Av.', street_id=1)>
<Place(id=21, name='Place 1, Second Av.', street_id=2)>
<Place(id=22, name='Place 2, Second Av.', street_id=2)>
但是应该是:
<Street(id=1, name='First Av.')>
<Place(id=11, name='Place 1, First Av.', street_id=1)>
<Place(id=12, name='Place 2, First Av.', street_id=1)>
<Street(id=2, name='Second Av.')>
<Place(id=21, name='Place 1, Second Av.', street_id=2)>
<Place(id=22, name='Place 2, Second Av.', street_id=2)>
街道上的每个地方。
谢谢。