【问题标题】:Flask-sqlalchemy ORM TypeError on a join连接上的 Flask-sqlalchemy ORM TypeError
【发布时间】:2021-08-30 19:18:06
【问题描述】:

我在将工作 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 命令在 DB 管理空间中完成其预期的工作(在物化视图中组合相关值以便快速查找),但我在 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

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

【问题讨论】:

  • 从您加入的查询中删除 .all()
  • 我试过了,但这会导致另一个问题:File "c:\users\...\app\models\mod_context.py", line 641, in ... db.select(Context_category). File "&lt;string&gt;", line 2, in select File "&lt;string&gt;", 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
  • 这看起来仍然是同样的错误
  • 抱歉,是的,同样的问题 - 我在想别的东西。基本上,它不能识别 Context_category 模型中的列。通过在原始 SQL 中显式指定列很容易避免此问题。

标签: python postgresql sqlalchemy orm flask-sqlalchemy


【解决方案1】:

请在下面找到一个快速而简单但完全有效的示例,您可以使用该示例来获得解决方案:

## 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

【讨论】:

  • 我会对此进行测试,但无论哪种方式,这都是很棒的帮助。谢谢!
  • 因为原始问题没有指定有关此查询的位置的任何内容,并且解决方案有效,所以我将答案标记为解决方案。但是,实际上,我不能使用它,因为它依赖于with app.app_context()。选择和连接应该是 MaterializedView class 定义的一部分,放置在由 app factory 拉取的模型模块中。因此,上下文不可用。我将完全应用不同的解决方案来呈现相同的数据。谢谢你的回答。这绝对是启发性的。
猜你喜欢
  • 2019-12-13
  • 2013-04-03
  • 2015-07-28
  • 1970-01-01
  • 1970-01-01
  • 2017-06-29
  • 2015-05-16
  • 1970-01-01
  • 2019-12-28
相关资源
最近更新 更多