没有外键的基于SQLAlchemy值的关系

时间:2018-10-01 16:48:31

标签: python sqlalchemy

我正在尝试将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)>

街道上的每个地方。

谢谢。

0 个答案:

没有答案