我有一组彼此相关的类
与招聘主题一对多 与 RecruitTable 进行一对多的招聘,RecruitTable 是其他几个类的抽象超类
这是类定义:
from typing import Optional, List
import sqlalchemy as sa
import sqlalchemy.orm as so
from app import db
class Subject(db.Model):
__tablename__ = 'subject'
id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, primary_key=True)
recruitments: so.WriteOnlyMapped[List['Recruitment']] = so.relationship(back_populates='subject')
class Recruitment(db.Model):
__tablename__ = 'recruitment'
id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, primary_key=True)
subject: so.Mapped['Subject'] = so.relationship('Subject', back_populates='recruitments')
evaluations: so.WriteOnlyMapped[List['RecruitTable']] = so.relationship('RecruitTable', back_populates='recruitment', cascade="all, delete-orphan")
class RecruitTable(db.Model):
__abstract__ = True
id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, primary_key=True)
@so.declared_attr
def id_recruitment(self) -> so.Mapped[int]:
return so.mapped_column(sa.Integer, sa.ForeignKey("recruitment.id"), index=True, nullable=False)
@so.declared_attr
def recruitment(self) -> so.Mapped[Recruitment]:
return so.relationship('Recruitment', back_populates='evaluations') )
class SubTable1(RecruitTable):
col1: so.Mapped[Optional[str]] = so.mapped_column(sa.String(16))
class SubTable2(RecruitTable):
col2: so.Mapped[Optional[str]] = so.mapped_column(sa.String(16))
这是我收到的错误
sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Triggering mapper: 'Mapper[Recruitment(recruitment)]'. Original exception was: When initializing mapper Mapper[Recruitment(recruitment)], expression 'RecruitTable' failed to locate a name ('RecruitTable'). If this is a class name, consider adding this relationship() to the <class 'app.database.models.Recruitment'> class after both dependent classes have been defined.
您遇到的错误通常发生在 SQLAlchemy 尝试在所有依赖类完全定义之前映射关系时。具体来说,当关系引用映射器尚未处理的类时,可能会发生这种情况。这种情况经常发生在抽象基类及其子类中。
要解决此问题,您应该确保 SQLAlchemy 在尝试映射关系之前完全了解所有子类。这可以通过在处理关系定义之前导入子类来实现。您还可以使用 mapper_args 属性来帮助解决此问题。
这是包含这些注意事项的更新代码:
from typing import Optional, List
import sqlalchemy as sa
import sqlalchemy.orm as so
from app import db
class Subject(db.Model):
__tablename__ = 'subject'
id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, primary_key=True)
recruitments: so.WriteOnlyMapped[List['Recruitment']] = so.relationship(back_populates='subject')
class Recruitment(db.Model):
__tablename__ = 'recruitment'
id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, primary_key=True)
subject_id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, sa.ForeignKey('subject.id'), nullable=False)
subject: so.Mapped['Subject'] = so.relationship('Subject', back_populates='recruitments')
evaluations: so.WriteOnlyMapped[List['RecruitTable']] = so.relationship('RecruitTable', back_populates='recruitment', cascade="all, delete-orphan")
class RecruitTable(db.Model):
__abstract__ = True
id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, primary_key=True)
@so.declared_attr
def id_recruitment(cls) -> so.Mapped[int]:
return so.mapped_column(sa.Integer, sa.ForeignKey("recruitment.id"), index=True, nullable=False)
@so.declared_attr
def recruitment(cls) -> so.Mapped['Recruitment']:
return so.relationship('Recruitment', back_populates='evaluations')
class SubTable1(RecruitTable):
__tablename__ = 'sub_table_1'
id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, primary_key=True)
col1: so.Mapped[Optional[str]] = so.mapped_column(sa.String(16))
class SubTable2(RecruitTable):
__tablename__ = 'sub_table_2'
id: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer, primary_key=True)
col2: so.Mapped[Optional[str]] = so.mapped_column(sa.String(16))
# Ensure all tables are imported/known to SQLAlchemy before defining relationships
db.configure_mappers()
主要变化:
__mapper_args__
用于确保 SQLAlchemy 了解表的多态性质,但对于这个特定示例来说并不是绝对必要的。通过确保 SQLAlchemy 在处理关系时具有所有必需的类定义,应该可以解决初始化错误。