英文:
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 DrawNumber
s 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)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论