插入 SQL Alchemy 中忽略了辅助连接条件

Secondary Join Condition Ignored in Insert SQL Alchemy

我有以下模型(我仍然需要添加一些适当的验证):

class Participant(db.Model):
    player_id = db.Column(db.Integer, db.ForeignKey('player.id'),
                          primary_key=True)
    game_id = db.Column(db.Integer, db.ForeignKey('game.id'), primary_key=True)
    winner = db.Column(db.Boolean)


class Player(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    avatar = db.Column(db.String(120))
    skill = db.Column(db.Integer)


def __repr__(self):
        return '<User %r>' % (self.name)


class Game(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    date = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    loser_score = db.Column(db.Integer)
    loser = db.relationship('Player',
                              secondary=Participant.__table__,
                              secondaryjoin=and_(
                                 Player.id == Participant.player_id,
                                 Participant.winner == False))
    winner = db.relationship('Player',
                             secondary=Participant.__table__,
                             secondaryjoin=and_(
                                 Player.id == Participant.player_id,
                                 Participant.winner == True))

    def __repr__(self):
        return '<Game %r>' % (self.id)

这些在我检索数据时非常有用。但是,当我插入一个 Game 对象时,它会忽略 Participant table 中的 'winner' 字段并生成这些 SQL 语句:

INFO:sqlalchemy.engine.base.Engine:
INSERT INTO game (date, loser_score) VALUES (?, ?)
('2015-02-13 19:31:04.804182', 15)
INSERT INTO participant (player_id, game_id) VALUES (?, ?)
(2, 2)
INSERT INTO participant (player_id, game_id) VALUES (?, ?)
(1, 2)

我希望它生成:

INFO:sqlalchemy.engine.base.Engine:
INSERT INTO game (date, loser_score) VALUES (?, ?)
('2015-02-13 19:31:04.804182', 15)
INSERT INTO participant (player_id, game_id, winner) VALUES (?, ?, 0)
(2, 2)
INSERT INTO participant (player_id, game_id, winner) VALUES (?, ?, 1)
(1, 2)

有人知道为什么会这样吗?

您无法 sqlalchemy 能够立即处理此类情况。下面的代码是我在这种情况下使用的代码:

class Player(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    avatar = db.Column(db.String(120))
    skill = db.Column(db.Integer)


class Participant(db.Model):
    player_id = db.Column(db.Integer, db.ForeignKey('player.id'),
                          primary_key=True)
    game_id = db.Column(db.Integer, db.ForeignKey('game.id'), primary_key=True)
    winner = db.Column(db.Boolean)

    # relationships
    player = db.relationship('Player')


class Game(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    date = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    loser_score = db.Column(db.Integer)

    # relationships
    game_players = db.relationship(
        'Participant',
        collection_class=attribute_mapped_collection("winner"),
        cascade="all, delete-orphan",
        backref="game",
    )

    # this allows usage like: my_game.player[True/False] = my_player
    players = association_proxy(
        'game_players', 'player',
        creator=lambda win, pla: Participant(winner=win, player=pla),
    )

    # winner: an alias to self.players[True]
    @property
    def winner(self):
        return self.players.get(True)

    @winner.setter
    def winner(self, value):
        self.players[True] = value

    @winner.deleter
    def winner(self):
        del self.players[True]

    # loser: an alias to self.players[False]
    @property
    def loser(self):
        return self.players.get(False)

    @loser.setter
    def loser(self, value):
        self.players[False] = value

    @loser.deleter
    def loser(self):
        del self.players[False]

允许您使用如下代码:

g1 = Game(...)
g1.players[True] = playerA
g1.players[False] = playerB

并且具有 winner/loser 甚至像这样的属性:

g1 = Game(...)
g1.winner = playerA
g1.loser = playerB

阅读 Proxying to Dictionary Based Collections 了解有关 attribute_mapped_collection 及其用法的更多信息。