【问题标题】:sqlalchemy ORM insert many to many objects in one transaction / change order of operationssqlalchemy ORM 在一个事务中插入多对多对象/更改操作顺序
【发布时间】:2023-02-05 00:57:23
【问题描述】:

我有这个代码 sn-p 用于 M2M 关系球员和火柴通过表埃洛.

SQLAlchemy==2.0.1

import os
from datetime import datetime
from uuid import UUID

from pytz import utc
from sqlalchemy import Column, create_engine, DateTime, ForeignKey, Integer
from sqlalchemy.orm import registry, relationship, Mapped, Session
from sqlalchemy import UUID as saUUID


mapper_registry = registry()


@mapper_registry.mapped
class Elo:
    __tablename__ = "elos"

    player: UUID = Column(saUUID, ForeignKey("players.id"), primary_key=True)
    match: UUID = Column(saUUID, ForeignKey("matches.id"), primary_key=True)
    elo: int = Column(Integer)


@mapper_registry.mapped
class Player:
    __tablename__ = "players"

    id: Mapped[UUID] = Column(saUUID, primary_key=True)

    matches: Mapped[list["Match"]] = relationship(secondary="elos", back_populates="players")


@mapper_registry.mapped
class Match:
    __tablename__ = "matches"

    id: Mapped[UUID] = Column(saUUID, primary_key=True)
    date: Mapped[datetime] = Column(DateTime, default=datetime.now(tz=utc))

    players: Mapped[list[Player]] = relationship(secondary="elos", back_populates="matches")

我想一次插入 3 个对象:Player、Match 和 Elo,但是 sqlalchemy 尝试先插入 Elo,所以它失败了,因为我的数据库中没有 Player 或 Match。我试过只使用 .add 而不是 .add_all 并且它给出了相同的结果。可能它想按字母顺序进行操作?

sqlalchemy.exc.IntegrityError: (psycopg2.errors.ForeignKeyViolation) insert or update on table "elos" violates foreign key constraint "elos_player_fkey"

那么如何更改 SQLAlchemy 中的操作顺序或者我应该怎么做? 提前致谢

if __name__ == '__main__':
    con_string = "postgresql://user:password@127.0.0.1:5432/db"
    engine = create_engine(con_string, echo=True)
    with Session(engine) as session:
        player = Player(id=UUID(bytes=os.urandom(16)))
        match = Match(id=UUID(bytes=os.urandom(16)))
        elo = Elo(player=player.id, match=match.id, elo=1999)
        session.add_all([player, match, elo])
        session.commit()

【问题讨论】:

  • player = Player(id=UUID(bytes=os.urandom(16)), matches=[Match(id=UUID(bytes=os.urandom(16)))]) 有效吗? (然后只是(session.add(player))
  • @snakecharmerb 所以现在我会让 Player 和 Match 对象不通过 Elo 表关联。但我想在单笔交易中完成
  • elos 记录是自动创建的(将 echo=True 添加到您的 create_engine 并检查生成的 SQL)
  • @snakecharmerb 我的映射有误吗?因为它没有。我应该将关系添加到我的 Elo 表吗?在那种情况下,如果它会自动执行,我该如何在 Elo 表中设置 elo 属性?

标签: python sqlalchemy


【解决方案1】:

一起创建Player和它的Match,自动创建多对多记录:

engine = sa.create_engine('postgresql:///test', echo=True)
mapper_registry.metadata.drop_all(engine, checkfirst=True)
mapper_registry.metadata.create_all(engine)

with Session(engine) as s, s.begin():
    player = Player(
               id=UUID(bytes=os.urandom(16)), 
               matches=[Match(id=UUID(bytes=os.urandom(16)))]
    )
    s.add(player)

输出:

2023-02-04 16:36:45,178 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2023-02-04 16:36:45,178 INFO sqlalchemy.engine.Engine INSERT INTO matches (id, date) VALUES (%(id)s::UUID, %(date)s)
2023-02-04 16:36:45,178 INFO sqlalchemy.engine.Engine [generated in 0.00008s] {'id': UUID('4c09a368-0b5d-dbf4-6f5a-062e136a34dc'), 'date': datetime.datetime(2023, 2, 4, 16, 36, 45, 135359, tzinfo=<UTC>)}
2023-02-04 16:36:45,179 INFO sqlalchemy.engine.Engine INSERT INTO players (id) VALUES (%(id)s::UUID)
2023-02-04 16:36:45,179 INFO sqlalchemy.engine.Engine [generated in 0.00006s] {'id': UUID('68d18c85-0d60-8636-c3b8-05e6ae9a68bb')}
2023-02-04 16:36:45,180 INFO sqlalchemy.engine.Engine INSERT INTO elos (player, match) VALUES (%(player)s::UUID, %(match)s::UUID)
2023-02-04 16:36:45,180 INFO sqlalchemy.engine.Engine [generated in 0.00007s] {'player': UUID('68d18c85-0d60-8636-c3b8-05e6ae9a68bb'), 'match': UUID('4c09a368-0b5d-dbf4-6f5a-062e136a34dc')}
2023-02-04 16:36:45,180 INFO sqlalchemy.engine.Engine COMMIT

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-26
    • 2012-10-23
    • 1970-01-01
    • 2017-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多