文章内容

2024/12/17 1:42:16,作 者: 黄兵

sqlalchemy.exc.InvalidRequestError: 'model' does not support object population - eager loading cannot be applied.

最近在使用 SQLAlchemy 的时候,出现了这个错误:

sqlalchemy.exc.InvalidRequestError: 'model' does not support object population - eager loading cannot be applied.

具体模型代码:

class Category(db.Model):
    __tablename__ = 'categories'
    __table_args__ = {'comment': 'API 文档目录'}
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64))
    parent_id = db.Column(db.Integer, db.ForeignKey('document_categories.id'), nullable=True,
                          comment='父类别ID')
    link_text = db.Column(db.String(128), index=True, unique=True, comment='文档类别链接')
    rank = db.Column(db.SmallInteger, comment='类别排名')
    type_id = db.Column(db.Integer, db.ForeignKey('document_category_types.id'))
    is_top_menu = db.Column(db.Boolean, comment='是否为顶部菜单')
    is_expandable_menu = db.Column(db.Boolean, comment='是否为可扩展菜单')
    title = db.Column(db.String(64), comment='文档目录标题')
    sub_title = db.Column(db.String(128), comment='文档目录副标题')
    documents = db.relationship('Document', backref='category', lazy='dynamic')
    introductions = db.relationship('DocumentCategoryIntroduction', backref='category', lazy='dynamic')

    # 自引用关系:一个类别可以有多个子类别,且每个类别可以有一个父类别
    parent = db.relationship('DocumentCategory', remote_side=[id],
                             backref=db.backref('subcategories', lazy='dynamic'))
出现错误的代码:
get_menu = (DocumentCategory.query.filter_by(type_id=get_category_type.id).filter(
    DocumentCategory.parent_id == None).order_by(DocumentCategory.rank).options(
    joinedload(DocumentCategory.subcategories)).all())

出现问题的原因:
在模型中subcategories 被定义为 lazy='dynamic',这种情况下,SQLAlchemy 无法通过 joinedload 进行预加载操作。

原因分析:
  • lazy='dynamic'
dynamic 懒加载模式返回一个查询对象,而不是实际的数据集合,因此无法用于 joinedload 进行预加载。
  • joinedload 的限制
joinedload 适用于 lazy='select'lazy='joined' 类型的关系,而不适用于 lazy='dynamic'

解决方案:
lazy='dynamic' 改为 lazy='selectlazy='joined',这样可以支持 joinedload 进行预加载。
# 修改 CloudDocumentCategory 模型
parent = db.relationship(
    'DocumentCategory',
    remote_side=[id],
    backref=db.backref('subcategories', lazy='select')  # 这里改成 'select' 或 'joined'
)
  • lazy='select':按需加载(默认行为)。
  • lazy='joined':在查询父记录时,直接使用 JOIN 加载子记录。
  • 修改后,代码中可以正常使用 joinedload
    get_menu = (
        CloudDocumentCategory.query
        .filter_by(type_id=get_category_type.id, parent_id=None)
        .order_by(CloudDocumentCategory.rank)
        .options(joinedload(CloudDocumentCategory.subcategories))  # 预加载子菜单
        .all()
    )
    

    黄兵个人博客原创。
    分享到:

    发表评论

    评论列表