插入 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
及其用法的更多信息。
我有以下模型(我仍然需要添加一些适当的验证):
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
及其用法的更多信息。