在flask sqlalchemy中设置多对多关系

时间:2014-05-21 08:01:49

标签: python sqlalchemy

我想制作一个游戏桌,每个游戏应该指向两个玩家或用户。

这是我现在的代码。我正在使用SQLAlchemy

UserGame = db.Table('UserGame',
    db.Column('game_id',db.Integer,db.ForeignKey('game.id')),
    db.Column('user_id',db.Integer,db.ForeignKey('user.id')),
    )

class User(db.Model):
    __tablename__ = 'User'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    email = db.Column(db.String(120), unique=True)
    games = db.relationship('Game', secondary=UserGame, backref='players')

    def __init__(self, username, email):
        self.username = username
        self.email = email
    def __repr__(self):
        return '<User %r>' % self.username

class Game(db.Model):
    __tablename__ = 'Game'
    id = db.Column(db.Integer, primary_key=True)
    player1 = db.Column(db.Integer,db.ForeignKey('User.id')
    player2 = db.Column(db.Integer,db.ForeignKey('User.id')
    ended = db.Column(db.Boolean)
    winner_username = db.Column(db.String(80),db.ForeignKey('User.username'))

但是,当我尝试创建用户时,出现以下错误:

sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child     tables on relationship User.games - there are no foreign keys linking these tables via secondary table 'UserGame'.  Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify 'primaryjoin' and 'secondaryjoin' expressions.

有什么想法吗?

1 个答案:

答案 0 :(得分:1)

M到N导致关联类

一旦找到M:N关系,您即将使用所谓的关联类来描述这种关系。

在你的情况下,你有两个玩家之间的关系,协会的意思是“他们一起玩的游戏”。

最小关联类描述:

  • 某些类的两个实例之间的关系
  • 没有其他数据绑定到给定关联

可能存在三元甚至更复杂的关联,但这些很少见。

在某些情况下,这就是Game的情况,我们可能会为该关联添加一些额外的数据(比如结果,谁是赢家等)。

描述同一类的两个实例之间的关联没有概念上的问题。

sqlalchemy中的建模关联类

有三种方法可以在sqlalchemy中建模关联类

次要表

在sqlalchemy中,简单的M:N关系通常由所谓的辅助表建模。不幸的是,这不是这种情况,因为我们需要将附加信息附加到已知的关联。

协会类

这是您的理由。代码将随之而来。

协会代理

有一些高级技术,使用association_proxy,可能会隐藏关联类实例,但我们不需要这个(实际上,我们希望在模型中看到游戏的概念)。

model.py - 使用的类

以下示例模型团队,可能会一起玩游戏。每场比赛都在主场进行,另一支球队被视为客人。我们会在每场比赛中记录一个分数。

此示例应易于根据您的方案进行修改。

from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Team(Base):
    __tablename__ = "team"
    id = Column(Integer, primary_key=True)
    name = Column(String(40))

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return "<Team: {self.name}>".format(self=self)

class Game(Base):
    __tablename__ = "game"
    host_id = Column(Integer, ForeignKey("team.id"), primary_key=True)
    guest_id = Column(Integer, ForeignKey("team.id"), primary_key=True)
    host_score = Column(Integer)
    guest_score = Column(Integer)

    def __init__(self, host, guest, host_score, guest_score):
        self.host = host
        self.guest = guest
        self.host_score = host_score
        self.guest_score = guest_score
    host = relationship(Team, lazy="joined", foreign_keys="Game.host_id", backref="games_ashost")
    guest = relationship(Team, lazy="joined", foreign_keys="Game.guest_id", backref="games_asguest")

    def __repr__(self):
        templ = "<Game: {self.host.name} vs {self.guest.name} score: {self.host_score}:{self.guest_score}>" 
        return templ.format(self=self)

除了设置课程Game之外,为此课程玷污hostguest肉类信息非常重要。我们为此目的使用relationship

由于我们正在关联同一个类的两个实例,因此sqlalchemy很难找到要与host链接的类属性以及与guest链接的类属性。为此,relationship必须使用foreign_keys,它可能以数组或一串外键的形式使用。 (这对我来说是最难的部分,因为例子没有显示这样的例子)。

backref中的{p> relationship会向合作班级添加新属性,因此backref="games_ashost会为每个Team属性添加Team().games_ashost

使用模型

导入所需内容

>>> from model import *
>>> from sqlalchemy import *
>>> from sqlalchemy.orm import Session

准备引擎和会话

>>> engine = create_engine("sqlite://")
>>> Base.metadata.create_all(engine)
>>> session = Session(engine)

创建团队alfa,beta,gama,delta

>>> alfa = Team("alfa")
>>> beta = Team("beta")
>>> gama = Team("gama")
>>> delta = Team("delta")
>>> alfa
<Team: alfa>
>>> beta
<Team: beta>
>>> gama
<Team: gama>
>>> delta
<Team: delta>

将它们存储在数据库中

>>> session.add_all([alfa, beta, gama, delta])
>>> session.commit()

实例化游戏(在Alfa和Beta以及Alfa和Gama之间)并将它们存储在数据库中

>>> game_ab = Game(alfa, beta, 0, 0)
>>> game_ab
<Game: alfa vs beta score: 0:0>
>>> game_ag = Game(alfa, gama, 0, 0)
>>> session.add_all([game_ab, game_ag])
>>> session.commit()

探索团队的可用属性

>>> alfa.games_ashost
[<Game: alfa vs beta score: 0:0>, <Game: alfa vs gama score: 0:0>]
>>> alfa.games_asguest
[]
>>> beta.games_ashost
[]
>>> beta.games_asguest
[<Game: alfa vs beta score: 0:0>]
>>> gama.games_ashost
[]
>>> gama.games_asguest
[<Game: alfa vs gama score: 0:0>]
>>> gama
<Team: gama>

探索游戏属性

>>> game_ab.host
<Team: alfa>
>>> game_ab.guest
<Team: beta>