连接上的 Flask-sqlalchemy ORM TypeError

Flask-sqlalchemy ORM TypeError on a join

我在将有效的 SQL 文本转换为 ORM 命令时遇到问题...

select ctx_j.name as cat_name, x.id as value_id, x.value_r1 as r1, 
    x.value_r2 as r2, x.value_r3 as r3, x.value_r4 as r4
from (
    select cv.id as id, cs.name as name 
    from context_values cv 
    left join context_categories cs 
    on cv.cat_id=cs.id
    where cv.verified is True
) as ctx_j
left join (
    select t3.id as id, value_r1, value_r2, value_r3, value_r4 
    from (((((
        context_values as v 
    left join context_value_r1 as r1 on v.id=r1.value_id) as t1 
    left join context_value_r2 as r2 on t1.id=r2.value_id) as t2
    left join context_value_r3 as r3 on t2.id=r3.value_id) as t3
    left join context_value_r4 as r4 on t3.id=r4.value_id
) as x on ctx_j.id=x.id
order by cat_name, value_id

来自以下型号:

class Context_value(db.Model):
    __tablename__ = 'context_values'
    id = db.Column(...)
    cat_id = db.Column(db.SmallInteger, db.ForeignKey('context_categories.id',     
        ondelete="RESTRICT", onupdate="CASCADE"), unique=False, index=True, nullable=False)
    verified = db.Column(db.Boolean)
    context_value_r1 = db.relationship('Context_value_r1', backref='context_values', lazy='dynamic')
    etc.

class Context_category(db.Model):
    __tablename__ = 'context_categories'
    id = db.Column(db.SmallInteger, primary_key=True)
    ...
    name = db.Column(db.String(100), nullable=False)
    __table_args__ = (db.Index('unq_context_categories_name', '...', 'name', unique=True),)

    ctx_values = db.relationship('Context_value', backref='context_categories', lazy='select')

SQL 命令在数据库管理员 space 中执行其预期的工作(在物化视图中组合相关值以进行快速查找),但我无法在ORM 语法。

db.join(
    db.select(Context_category).
    outerjoin(Context_category.ctx_values).
    where(Context_value.verified==True
    ),
    db.select(Context_value).
    outerjoin(Context_value.context_rel1).
    outerjoin(Context_value.context_rel2).
    outerjoin(Context_value.context_rel3).
    outerjoin(Context_value.context_rel4).
    all(),
    isouter=True
    )

生成以下错误(仅消息结尾):

  File "c:\users\...\app\models\mod_context.py", line 638, in ...
    db.select(Context_category).\
  File "<string>", line 2, in select
  File "<string>", line 2, in __init__
  File "c:\users\...\venv\lib\site-packages\sqlalchemy\util\deprecations.py", line 139, in warned
    return fn(*args, **kwargs)
  File "c:\users\...\venv\lib\site-packages\sqlalchemy\sql\selectable.py", line 3114, in __init__
    for c in columns:
TypeError: 'DefaultMeta' object is not iterable

任何帮助或建议将不胜感激。

请在下面找到一个快速但完整的示例,您可以使用它来找到您的解决方案:

## Imports

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

## Configuration

app = Flask(__name__)
_db_uri = "sqlite:///:memory:"
app.config["SQLALCHEMY_DATABASE_URI"] = _db_uri
app.config["SQLALCHEMY_ECHO"] = True
db = SQLAlchemy(app)


# ## Model definitions
class Context_value(db.Model):
    __tablename__ = "context_values"
    id = db.Column(db.Integer, primary_key=True)
    cat_id = db.Column(
        db.SmallInteger,
        db.ForeignKey("context_categories.id", ondelete="RESTRICT", onupdate="CASCADE"),
        unique=False,
        index=True,
        nullable=False,
    )
    verified = db.Column(db.Boolean)

    context_categories = db.relationship(
        "Context_category", back_populates="ctx_values"
    )

    context_rel1 = db.relationship(
        "Context_value_r1", backref="context_values", lazy="dynamic"
    )

    context_rel2 = db.relationship(
        "Context_value_r2", backref="context_values", lazy="dynamic"
    )


class Context_category(db.Model):
    __tablename__ = "context_categories"
    id = db.Column(db.SmallInteger, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    # __table_args__ = (db.Index('unq_context_categories_name', '...', 'name', unique=True),)

    ctx_values = db.relationship(
        "Context_value", back_populates="context_categories", lazy="select"
    )


class Context_value_r1(db.Model):
    __tablename__ = "context_value_r1"
    id = db.Column(db.Integer, primary_key=True)
    value_id = db.Column(
        db.ForeignKey("context_values.id", ondelete="RESTRICT", onupdate="CASCADE"),
        unique=False,
        index=True,
        nullable=False,
    )
    value_r1 = db.Column(db.Integer)


class Context_value_r2(db.Model):
    __tablename__ = "context_value_r2"
    id = db.Column(db.Integer, primary_key=True)
    value_id = db.Column(
        db.ForeignKey("context_values.id", ondelete="RESTRICT", onupdate="CASCADE"),
        unique=False,
        index=True,
        nullable=False,
    )
    value_r2 = db.Column(db.Integer)


def _main():
    with app.app_context():
        db.drop_all()
        db.create_all()
        print("=" * 80)

        # aliases for readability
        cv = Context_value
        cs = Context_category
        ctx_j = (
            db.select(cv.id, cs.name)
            .select_from(cv)
            .outerjoin(cs, cv.context_categories)
            .where(cv.verified == True)
        ).subquery("ctx_j")

        # aliases for readability
        v = Context_value
        r1 = Context_value_r1
        r2 = Context_value_r2
        # sub-select
        x = (
            db.select(
                v.id.label("value_id"),
                r1.value_r1.label("r1"),
                r2.value_r2.label("r2"),
            )
            .select_from(v)
            .outerjoin(r1, v.context_rel1)
            .outerjoin(r2, v.context_rel2)
        ).subquery("x")

        # final query
        q = (
            db.select(ctx_j, x)
            .outerjoin(x, ctx_j.c.id == x.c.value_id)
            .order_by(ctx_j.c.name, x.c.value_id)
        )

        print(q)


if __name__ == "__main__":
    _main()

q 的打印输出应如下所示:

SELECT ctx_j.id,
       ctx_j.name,
       x.value_id,
       x.r1,
       x.r2
FROM
  (SELECT context_values.id AS id,
          context_categories.name AS name
   FROM context_values
   LEFT OUTER JOIN context_categories ON context_categories.id = context_values.cat_id
   WHERE context_values.verified = true) AS ctx_j
LEFT OUTER JOIN
  (SELECT context_values.id AS value_id,
          context_value_r1.value_r1 AS r1,
          context_value_r2.value_r2 AS r2
   FROM context_values
   LEFT OUTER JOIN context_value_r1 ON context_values.id = context_value_r1.value_id
   LEFT OUTER JOIN context_value_r2 ON context_values.id = context_value_r2.value_id) AS x ON ctx_j.id = x.value_id
ORDER BY ctx_j.name,
         x.value_id