You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask+Flask-SQLAlchemy+sqlalchemy_mptt插入根节点报错求助

Flask-SQLAlchemy + sqlalchemy_mptt 插入根节点报错解决方法

问题描述

使用Flask、Flask-SQLAlchemy和sqlalchemy_mptt构建嵌套集表时,在flask shell中插入根节点触发sqlalchemy.exc.ArgumentError,错误提示为:

期望列表达式、FROM子句或其他列子句元素,却得到了BinaryExpression对象。

报错堆栈

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/orm/scoping.py", line 553, in commit
    return self._proxied.commit()
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 1903, in commit
    trans.commit(_to_root=True)
  File "<string>", line 2, in commit
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/orm/state_changes.py", line 137, in _go
    ret_value = fn(self, *arg, **kw)
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 1218, in commit
    self._prepare_impl()
  File "<string>", line 2, in _prepare_impl
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/orm/state_changes.py", line 137, in _go
    ret_value = fn(self, *arg, **kw)
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 1193, in _prepare_impl
    self.session.flush()
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 4155, in flush
    self._flush(objects)
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 4292, in _flush
    transaction.rollback(_capture_exception=True)
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/util/langhelpers.py", line 147, in __exit__
    raise exc_value.with_traceback(exc_tb)
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 4252, in _flush
    flush_context.execute()
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/orm/unitofwork.py", line 467, in execute
    rec.execute(self)
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/orm/unitofwork.py", line 644, in execute
    util.preloaded.orm_persistence.save_obj(
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/orm/persistence.py", line 54, in save_obj
    save_obj(base_mapper, [state], uowtransaction, single=True)
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/orm/persistence.py", line 60, in save_obj
    for (
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/orm/persistence.py", line 233, in _organize_states_for_save
    mapper.dispatch.before_insert(mapper, connection, state)
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/event/attr.py", line 487, in __call__
    fn(*args, **kw)
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/orm/events.py", line 889, in wrap
    fn(*arg, **kw)
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy_mptt/events.py", line 574, in before_insert
    mptt_before_insert(mapper, connection, instance)
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy_mptt/events.py", line 96, in mptt_before_insert
    select(
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/sql/_selectable_constructors.py", line 493, in select
    return Select(*entities)
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/sql/selectable.py", line 5161, in __init__
    self._raw_columns = [
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/sql/selectable.py", line 5162, in <listcomp>
    coercions.expect(
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/sql/coercions.py", line 413, in expect
    resolved = impl._literal_coercion(
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/sql/coercions.py", line 652, in _literal_coercion
    self._raise_for_expected(element, argname)
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/sql/coercions.py", line 1143, in _raise_for_expected
    return super()._raise_for_expected(
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/sql/coercions.py", line 711, in _raise_for_expected
    super()._raise_for_expected(
  File "/Users/pingguo/opt/anaconda3/envs/Flask_Web/lib/python3.8/site-packages/sqlalchemy/sql/coercions.py", line 536, in _raise_for_expected
    raise exc.ArgumentError(msg, code=code) from err
sqlalchemy.exc.ArgumentError: Column expression, FROM clause, or other columns clause element expected, got [<sqlalchemy.sql.elements.BinaryExpression object at 0x7fee25170d90>]. Did you mean to say select(<sqlalchemy.sql.elements.BinaryExpression object at 0x7fee25170d90>)?

代码示例

from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy_mptt.mixins import BaseNestedSets
import os

app = Flask(__name__)
url = 'sqlite:////' + os.path.join(app.root_path, 'data.db')
app.config['SQLALCHEMY_DATABASE_URI'] = url
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False 

db = SQLAlchemy(app)

class Category(db.Model, BaseNestedSets):
    __tablename__ = 'category'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)

    def __repr__(self):
        return '<Category {}>'.format(self.name)

解决方法

原因

官方sqlalchemy_mptt库未适配SQLAlchemy 2.0+版本的语法变更,插入根节点时查询最大tree_id的代码使用了旧版SQLAlchemy的select写法,导致在2.0+环境中触发参数类型错误。

方案1:降级SQLAlchemy版本

安装兼容的旧版SQLAlchemy和Flask-SQLAlchemy:

pip install sqlalchemy==1.4.49 flask-sqlalchemy==2.5.1

方案2:修改sqlalchemy_mptt源码

找到环境中sqlalchemy_mptt/events.py文件,定位到mptt_before_insert函数中查询最大tree_id的代码段(约第96行),将原来的列表式select调用修改为单个表达式:

  • 原代码(错误写法):
    select(
        [func.max(node_class.tree_id) + 1]
    ).where(
        node_class.tree_id is not None
    )
    
  • 修改后:
    select(
        func.max(node_class.tree_id) + 1
    ).where(
        node_class.tree_id is not None
    )
    

方案3:使用适配SQLAlchemy 2.0的分支版本

安装第三方维护的sqlalchemy-mptt2库,该库已适配SQLAlchemy 2.0+:

pip install sqlalchemy-mptt2

同时修改代码中的导入语句:

# 替换原导入
from sqlalchemy_mptt2.mixins import BaseNestedSets

内容的提问来源于stack exchange,提问作者Plain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 21:33:12