我正在为当地一个小型社区的项目做志愿者,同时学习 Python。这是我第一次使用 Flask 和 SQLAlchemy(MySQL 用于存储数据)。我选择的 ORM 一开始工作得很好,但是当我尝试按照这个官方指南在两个表之间切换到多对多模型时,这个错误“NoReferencedTableError”在主 Flask 应用程序的初始化期间开始出现。
这是完整的错误行:
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'assoc_tbl_1_user_X_challenge.challengeId' could not find table 'challengesTbl' with which to generate a foreign key to target column 'id'
无论我多么努力,我都无法发现我的定义中的错误。我试图用谷歌搜索这个错误,但其他情况似乎是关于人们在表或类名中犯了一些拼写错误 - 我仔细检查过,这里没有拼写错误。我开始怀疑这可能与以下事实有关:
association_table1
变量是在定义其他表的类之前定义的,并且在实例化它时找不到它们。但即使我将它移动到文件末尾,它仍然不起作用,因为它是从其他表类的定义中引用的。首先声明它并在文件末尾初始化也没有帮助。
我还注意到,当我尝试运行使用相同 ORM 文件预初始化数据库的外部脚本时,似乎只创建了三个表:
mysql> show tables;
+------------------------------+
| Tables_in_nekoko$ABsitedb |
+------------------------------+
| challengesTbl |
| rideRecordingsTbl |
| usersTbl |
+------------------------------+
3 rows in set (0.00 sec)
我没有看到关联表
assoc_tbl_1_user_X_challenge
,不是应该同时创建吗?
我当前的数据模型是这样的:
from . import db
from flask_login import UserMixin
from sqlalchemy import text
class Base(db.DeclarativeBase):
pass
association_table1 = db.Table(
"assoc_tbl_1_user_X_challenge",
Base.metadata,
db.Column("userId", db.ForeignKey("usersTbl.id"), primary_key=True),
db.Column("challengeId", db.ForeignKey("challengesTbl.id"), primary_key=True),
)
class User(UserMixin, db.Model):
__tablename__ = "usersTbl"
id = db.Column(db.Integer, primary_key=True) # primary keys are required by SQLAlchemy
email = db.Column(db.String(100), unique=True)
password = db.Column(db.String(1000))
givenName = db.Column(db.String(100))
sn = db.Column(db.String(100))
totalDistanceKm = db.Column(db.Float, server_default=text("0.0"))
groups = db.Column(db.JSON, nullable=False)
isActive = db.Column(db.Boolean, default=True, nullable=False, index=True)
challenges = db.relationship('Challenge', secondary=association_table1, back_populates="participants")
rides = db.relationship('Ride', backref='usersTbl', lazy=True)
def __repr__(self):
return f'<User "{self.email}">'
class Ride(db.Model):
__tablename__ = "rideRecordingsTbl"
id = db.Column(db.Integer, primary_key=True)
rideRecording = db.Column(db.LargeBinary)
#rideRecording = db.Column(db.LONGBLOB, nullable=False)
rideName = db.Column(db.String(150))
distanceKm = db.Column(db.Float, server_default=text("0.0"))
duration = db.Column(db.Time)
uploadDateTime = db.Column(db.DateTime)
stream_hash = db.Column(db.String(1000))
ownerId = db.Column(db.Integer, db.ForeignKey('usersTbl.id'), nullable=False)
def __repr__(self):
return f'<Ride "{self.rideName[:20]}...">'
class Challenge(db.Model):
__tablename__ = "challengesTbl"
id = db.Column(db.Integer, primary_key=True)
rideRecording = db.Column(db.LargeBinary)
challengeName = db.Column(db.String(150))
challengeTargetDistanceKm = db.Column(db.Float, server_default=text("0.0"))
challengeCurrnetTotalDistanceKm = db.Column(db.Float, server_default=text("0.0"))
challengeEnds = db.Column(db.DateTime)
challengeStarts = db.Column(db.DateTime)
createdByUserId = db.Column(db.Integer)
isActive = db.Column(db.Boolean, default=True, nullable=False, index=True)
participants = db.relationship('User', secondary=association_table1, back_populates="challenges")
def __repr__(self):
return f'<Challenge "{self.challengeName[:20]}...">'
您提供的错误通常是由于定义表和关系的顺序而发生的。
如果您在其他表之前定义关联表,SQLAlchemy 可能不具备正确解析外键所需的所有信息。您需要确保在定义关联表本身之前定义关联表中的外键引用的表。
因此,您可以使用带有
Base
的声明性基本方法来确保正确的排序,然后在已定义 User
和 Challenge
类的类定义中定义关联表。
因此,首先定义
association
after 定义 User
和 Challenge
类。然后,对于关系也是如此:在定义 User.challenges
后添加 Challenge.participants
和 association_table1
。
所以,你的最终代码应该看起来或多或少像这样:
from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin
from sqlalchemy import text
db = SQLAlchemy()
class Base(db.Model):
#this will make the base class abstract, so it won't be mapped to a table.
__abstract__ = True
#define User and Challenge classes before the association table
class User(UserMixin, Base):
__tablename__ = "usersTbl"
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(100), unique=True)
password = db.Column(db.String(1000))
givenName = db.Column(db.String(100))
sn = db.Column(db.String(100))
totalDistanceKm = db.Column(db.Float, server_default=text("0.0"))
groups = db.Column(db.JSON, nullable=False)
isActive = db.Column(db.Boolean, default=True, nullable=False, index=True)
rides = db.relationship('Ride', backref='user', lazy=True)
def __repr__(self):
return f'<User "{self.email}">'
class Ride(Base):
__tablename__ = "rideRecordingsTbl"
id = db.Column(db.Integer, primary_key=True)
rideRecording = db.Column(db.LargeBinary)
rideName = db.Column(db.String(150))
distanceKm = db.Column(db.Float, server_default=text("0.0"))
duration = db.Column(db.Time)
uploadDateTime = db.Column(db.DateTime)
stream_hash = db.Column(db.String(1000))
ownerId = db.Column(db.Integer, db.ForeignKey('usersTbl.id'), nullable=False)
def __repr__(self):
return f'<Ride "{self.rideName[:20]}...">'
class Challenge(Base):
__tablename__ = "challengesTbl"
id = db.Column(db.Integer, primary_key=True)
rideRecording = db.Column(db.LargeBinary)
challengeName = db.Column(db.String(150))
challengeTargetDistanceKm = db.Column(db.Float, server_default=text("0.0"))
challengeCurrentTotalDistanceKm = db.Column(db.Float, server_default=text("0.0"))
challengeEnds = db.Column(db.DateTime)
challengeStarts = db.Column(db.DateTime)
createdByUserId = db.Column(db.Integer)
isActive = db.Column(db.Boolean, default=True, nullable=False, index=True)
def __repr__(self):
return f'<Challenge "{self.challengeName[:20]}...">'
#define association table after User and Challenge classes
association_table1 = db.Table(
"assoc_tbl_1_user_X_challenge",
Base.metadata,
db.Column("userId", db.Integer, db.ForeignKey("usersTbl.id"), primary_key=True),
db.Column("challengeId", db.Integer, db.ForeignKey("challengesTbl.id"), primary_key=True),
)
#add relationships after defining the association table
User.challenges = db.relationship('Challenge', secondary=association_table1, back_populates="participants")
Challenge.participants = db.relationship('User', secondary=association_table1, back_populates="challenges")
让我更新,希望它有效。