SQLAlchemy - 最新的多对多关系

SQLAlchemy - Latest of many to many relationship

我目前正在构建一个 API(使用 SQLAlchemy、FastAPI 和 Pydantic)来存储有关公寓及其居民的信息。一间公寓一次只能由一位居民入住,但我们会存储历史记录。数据模型如下:

class Resident(Base):
    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True, nullable=False)
    name = Column(String(200))


class Apartment(Base):
    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True, nullable=False)
    address = Column(String(200))

    residents = relationship("Occupation")


class Occupation(Base):
    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True, nullable=False)

    start = Column(Date)
    end = Column(Date)

    resident_id = Column(UUID(as_uuid=True), ForeignKey("residents.id"))
    resident = relationship("Resident")

    apartment_id = Column(UUID(as_uuid=True), ForeignKey("apartments.id"))
    apartment = relationship("Apartment")

公寓的 'residents' 关系将给我一个与职业的 1-N 关系。但我想要的是一种关系,它以 1-1 的方式为我提供给定公寓的当前居民。

以下查询用于获取所有公寓和业主:

db.query(models.Apartment).options(
    joinedload(models.Apartment.residents).joinedload(models.Occupation.resident),
).all()

使用 Pydantic 模式,这转化为以下 JSON:

[
    {
        "id": "4c260b11-0b93-4e68-a931-5facfb6bac52",
        "address": "X",
        "residents": [
            {
                "start": "2021-01-01",
                "end": "2021-12-31",
                "resident": {
                    "id": "982f6398-5213-4b07-8187-2d5606ee4142",
                    "name": "Steve Jobs"
                }
            }
        ]
    }
]

但我正在寻找的是:

[
    {
        "id": "4c260b11-0b93-4e68-a931-5facfb6bac52",
        "address": "X",
        "current_resident": {
            "id": "982f6398-5213-4b07-8187-2d5606ee4142",
            "name": "Steve Jobs"
        }
    }
]

是否有 SQLAlchemy 方法可以实现此目的?

您可以做的是向 Apartment 添加一个额外的 viewonly 关系,您在其中过滤当前日期:

today = datetime.date.today().strftime('%Y-%m-%d')
current_resident = relationship("Resident",
        secondary="occupation",
        primaryjoin=f'and_(Occupation.apartment_id == Apartment.id, \
                           Occupation.start <= "{today}", \
                           Occupation.end > "{today}")',
        secondaryjoin="Occupation.resident_id == Resident.id",
        uselist=False,
        viewonly=True
        )

此returns当前居住者的Resident对象或None当前日期没有居住者