J'ai deux modèles : Game et Player. Je veux avoir une liste de tous les joueurs du modèle de jeu, ainsi que l'un de ces joueurs dans un domaine différent. C'est un serveur de flacons et une base de données sqlite.

Voici mon modèle de joueur :

class Player(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    # other fields...
    
    game_id = db.Column(db.Integer, db.ForeignKey('game.id'))
    game = db.relationship('Game', back_populates='players', foreign_keys=game_id)

Voici mon modèle de jeu :

class Game(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    # other fields...

    current_president_id = db.Column(db.Integer, db.ForeignKey('player.id'))
    current_president = db.relationship("Player", foreign_keys=current_president_id)

    # other one-to-one relationships to the Player Model, exactly like the first one...

    players = db.relationship('Player', back_populates='game', foreign_keys=Player.game_id)

J'ai créé les deux modèles sur la base de cette réponse. Mais j'obtiens toujours le même avertissement :

SAWarning: Cannot correctly sort tables; there are unresolvable cycles between tables "game, player", which is usually caused by mutually dependent foreign key constraints.  Foreign key constraints involving these tables will not be considered; this warning may raise an error in a future release.

Comme le dit l'avertissement, lors de l'utilisation de cette configuration, j'obtiens alors à un moment donné :

sqlalchemy.exc.CircularDependencyError: Circular dependency detected. (SaveUpdateState(<Game at 0x23009e00160>), SaveUpdateState(<Player at 0x23009e141c0>), ProcessState(ManyToOneDP(Game.current_president), <Game at 0x23009e00160>, delete=False), ProcessState(OneToManyDP(Game.players), <Game at 0x23009e00160>, delete=False))

Je ne sais pas quoi faire, j'ai essayé de lire la documentation et testé de nombreuses autres configurations, mais rien n'y fait. Toute aide grandement appréciée. Merci!

3
Tibor Völcker 12 nov. 2020 à 19:32

1 réponse

Meilleure réponse

J'ai donc essayé d'apprendre un peu plus SQLAlchemy et j'ai trouvé une solution. Tout d'abord, définissez l'indicateur use_alter sur True dans la relation un-à-un :

current_president_id = db.Column(db.Integer, db.ForeignKey('player.id', use_alter=True))

Cela fait disparaître l'avertissement. Mais vous devez toujours faire attention maintenant. Étant donné que le modèle Player a une colonne référençant le Game.id et le modèle Game a une colonne référençant le Player.id, vous ne pouvez pas déclarer les relations dans un seul commit :

g, p1, p2 = Game(), Player(), Player()
db.session.add_all([g, p1, p2])
db.session.commit()
g.players = [p1, p2]
g.current_president = p3
db.session.commit()

Cela augmentera la CircularDependencyError. Mais cela ne sera pas :

g, p1, p2 = Game(), Player(), Player()
db.session.add_all([g, p1, p2])
db.session.commit()
g.players = [p1, p2]
db.session.commit()
g.current_president = p3
db.session.commit()

ÉDITER:

Utilisez use_alter=True dans la relation pour éviter toute CircularDependencyErrors. De cette façon, lorsque vous ajoutez un jeu avec des joueurs et la relation dans un commit, la relation sera définie dans une deuxième instruction 'ALTER' pour empêcher la dépendance circulaire.

0
Tibor Völcker 5 janv. 2021 à 18:30