如何最好地实现一个子属性,它是一个整数列表。

huangapple go评论107阅读模式
英文:

How best to implement a child property that is a list of integers

问题

我有一个1-N的关系,其中子属性只是一个整数列表。类似于:

class LotteryDraw(Base):
    __tablename__ = "draw"

    id: Mapped[int] = mapped_column(primary_key=True)
    draw_time: Mapped[dt.datetime]

    numbers: Mapped[List["DrawNumber"]] = relationship(
        back_populates="draw", cascade="all, delete-orphan"
    )

    def __repr__(self):
        return f"Draw(id={self.id!r}, draw_time={self.draw_time})"

class DrawNumber(Base):
    __tablename__ = "draw_numbers"

    id: Mapped[int] = mapped_column(primary_key=True)
    number: Mapped[int]
    draw_id: Mapped[int] = mapped_column(ForeignKey("draw.id"))

    draw: Mapped["LotteryDraw"] = relationship(back_populates="numbers")

    def __repr__(self):
        return f"{self.number}"

with Session(engine) as session:
    draw = LotteryDraw(
        draw_time=dt.datetime.now(),
        numbers=[DrawNumber(number=1), DrawNumber(number=39), DrawNumber(number=45), DrawNumber(number=46)],
        # 我想要在我的代码中假装它们只是普通的整数,无论是用于获取还是设置LotteryDraw.numbers都可以。(当直接使用draw_numbers表时,它们仍然是DrawNumbers,我主要是为了简化与LotteryDraw的操作。)
    )
    session.add_all([draw])
    session.commit()

如您所见,与LotteryDraw的numbers属性一起工作需要详细实例化:DrawNumber(number=N)。在我的代码中是否有可能假装它们只是普通整数,无论是获取还是设置LotteryDraw.numbers?(我不介意当直接使用draw_numbers表时它们仍然是DrawNumbers,我主要是为了简化与LotteryDraw的操作。)

英文:

I have a 1-N relationship where the child property is just a list of integers. Something like:

class LotteryDraw(Base):
    __tablename__ = "draw"

    id: Mapped[int] = mapped_column(primary_key=True)
    draw_time: Mapped[dt.datetime]

    numbers: Mapped[List["DrawNumber"]] = relationship(
        back_populates="draw", cascade="all, delete-orphan"
    )

    def __repr__(self):
        return f"Draw(id={self.id!r}, draw_time={self.draw_time})"

class DrawNumber(Base):
    __tablename__ = "draw_numbers"

    id: Mapped[int] = mapped_column(primary_key=True)
    number: Mapped[int]
    draw_id: Mapped[int] = mapped_column(ForeignKey("draw.id"))

    draw: Mapped["LotteryDraw"] = relationship(back_populates="numbers")

    def __repr__(self):
        return f"{self.number}"

with Session(engine) as session:
    draw = LotteryDraw(
        draw_time=dt.datetime.now(),
        numbers=[DrawNumber(number=1), DrawNumber(number=39), DrawNumber(number=45), DrawNumber(number=46)],
        # I would like to be able to do this instead: numbers=[1, 39, 45, 46],
    )
    session.add_all([draw])
    session.commit()

As you can see, working with the numbers property of a LotteryDraw requires spelling out the full instantiation: DrawNumber(number=N). Is it possible to pretend in my code that they are just plain integers, both for getting and setting LotteryDraw.numbers? (I don't mind having them as DrawNumbers when working just with the draw_numbers table directly, I'm mainly after simplifying working with LotteryDraw.)

答案1

得分: 1

你可以使用 AssociationProxy 来实现这个功能:

from __future__ import annotations

import datetime
from typing import Annotated

from sqlalchemy import ForeignKey, create_engine
from sqlalchemy.ext.associationproxy import AssociationProxy, association_proxy
from sqlalchemy.orm import (DeclarativeBase, Mapped, MappedAsDataclass,
                            mapped_column, relationship, sessionmaker)

DATABASE_URL = "postgresql+psycopg2://postgres:test_password@localhost:5432/test_db"

engine = create_engine(
    DATABASE_URL,
    echo=True,
)
Session = sessionmaker(
    bind=engine,
)


class Base(MappedAsDataclass, DeclarativeBase):
    pass


timestamp_auto = Annotated[
    datetime.datetime,
    mapped_column(
        default=datetime.datetime.now,
    ),
]


class LotteryDraw(Base):
    __tablename__ = "draw"

    id: Mapped[int] = mapped_column(primary_key=True, init=False)
    draw_time: Mapped[timestamp_auto] = mapped_column(init=False)

    numbers: Mapped[list[DrawNumber]] = relationship(
        back_populates="draw",
        cascade="all, delete-orphan",
        init=False,
        repr=False,
    )

    values: AssociationProxy[list[int]] = association_proxy(
        "numbers",
        "number",
        init=False,
    )


class DrawNumber(Base):
    __tablename__ = "draw_number"

    id: Mapped[int] = mapped_column(primary_key=True, init=False)
    number: Mapped[int]
    draw_id: Mapped[int] = mapped_column(ForeignKey("draw.id"), init=False)

    draw: Mapped[LotteryDraw] = relationship(
        back_populates="numbers",
        init=False,
        repr=False,
    )


if __name__ == "__main__":
    Base.metadata.create_all(engine)

    with Session() as session:
        draw = LotteryDraw()
        draw.values = [1, 39, 45, 46]
        session.add(draw)
        session.commit()

        print(draw)
英文:

You can use an AssociationProxy to do this:

from __future__ import annotations

import datetime
from typing import Annotated

from sqlalchemy import ForeignKey, create_engine
from sqlalchemy.ext.associationproxy import AssociationProxy, association_proxy
from sqlalchemy.orm import (DeclarativeBase, Mapped, MappedAsDataclass,
                            mapped_column, relationship, sessionmaker)

DATABASE_URL = "postgresql+psycopg2://postgres:test_password@localhost:5432/test_db"

engine = create_engine(
    DATABASE_URL,
    echo=True,
)
Session = sessionmaker(
    bind=engine,
)


class Base(MappedAsDataclass, DeclarativeBase):
    pass


timestamp_auto = Annotated[
    datetime.datetime,
    mapped_column(
        default=datetime.datetime.now,
    ),
]


class LotteryDraw(Base):
    __tablename__ = "draw"

    id: Mapped[int] = mapped_column(primary_key=True, init=False)
    draw_time: Mapped[timestamp_auto] = mapped_column(init=False)

    numbers: Mapped[list[DrawNumber]] = relationship(
        back_populates="draw",
        cascade="all, delete-orphan",
        init=False,
        repr=False,
    )

    values: AssociationProxy[list[int]] = association_proxy(
        "numbers",
        "number",
        init=False,
    )


class DrawNumber(Base):
    __tablename__ = "draw_number"

    id: Mapped[int] = mapped_column(primary_key=True, init=False)
    number: Mapped[int]
    draw_id: Mapped[int] = mapped_column(ForeignKey("draw.id"), init=False)

    draw: Mapped[LotteryDraw] = relationship(
        back_populates="numbers",
        init=False,
        repr=False,
    )


if __name__ == "__main__":
    Base.metadata.create_all(engine)

    with Session() as session:
        draw = LotteryDraw()
        draw.values = [1, 39, 45, 46]
        session.add(draw)
        session.commit()

        print(draw)

huangapple
  • 本文由 发表于 2023年7月28日 05:12:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/76783437.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定