【问题标题】:Sqlalchemy Filter condition always returns True in subquerySqlalchemy 过滤条件在子查询中总是返回 True
【发布时间】:2013-10-10 16:40:22
【问题描述】:

我使用flask-sqlalchemy 发表了这个声明,并且我选择保留它的原始形式。 Post.query 等价于session.query(Post)

我尝试创建一个子查询,该子查询将过滤掉数据库中处于草稿状态且不是由当前用户创建或修改的所有帖子。我做了这个查询,

Post.query\
    .filter(sqlalchemy.and_(
        Post.post_status != Consts.PostStatuses["Draft"],
            sqlalchemy.or_(
                Post.modified_by_id == current_user.get_id(),
                Post.created_by_id == current_user.get_id()))

创建者:

Where true AND ("Post".modified_by_id = :modified_by_id_1 OR  
"Post".created_by_id = :created_by_id_1)

预期结果:

Where "Post".post_status != "Draft" AND (
"Post".modified_by_id = :modified_by_id_1 OR  
"Post".created_by_id = :created_by_id_1)

我想知道,为什么会这样?如何提高 SQLAlchemy 中的错误级别?我认为我的项目正在悄然失败,我想证实我的猜测。

更新:

我使用了错误的常量字典。一个字典包含整数,另一个包含字符串(一个用于数据库查询,一个用于打印)。

_post_status = db.Column(
        db.SmallInteger,
        default=Consts.post_status["Draft"]) 

post_status 包含整数,Consts.PostStatuses 包含字符串。事后看来,真是个坏主意。我将制作一个返回元组而不是两个字典的字典。

@property
def post_status(self):
    return Consts.post_status.get(getattr(self, "_post_status", None))

【问题讨论】:

  • Post.post_status 和 Consts.PostStatuses["Draft"] 是什么类型? Post是模特吗? SqlAlchemy 基于类型和 python 魔术方法构建查询。我可以肯定地告诉你,如果 post_status 是布尔值并且草稿是空字符串,sqlalchemy 将生成一个始终为真的 sql 语句。肯定还有其他情况。
  • post_status 是一个小整数,PostStatuses 是一个整数(在本例中为 1)。
  • 你能更精确一点吗?在运行查询之前添加print type(Post.post_status), type(Const.PostStatuses["Draft"])。它应该打印类似<class 'sqlalchemy.orm.attributes.InstrumentedAttribute'> int
  • 不确定如果双方都是 int 类型,您期望如何获得"Post".post_status != "Draft"。我必须假设您有一个错误输入的比较,它的评估结果总是正确的。请按照 deque 的说明向我们提供更多信息。
  • 所以Type(Post.post_status)<type 'property'>Const.PostStatuses["Draft"]<type 'str'>。我忘记了我有两个状态常量字典。一个包含字符串,一个包含整数。我用的是包含字符串的那个。

标签: python sqlalchemy flask-sqlalchemy


【解决方案1】:

问题是您的 post_status 属性不能用于 ORM 级别的查询,因为这是一个 python descriptor,默认情况下在类级别返回自身:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class A(Base):
    __tablename__ = 'a'

    id = Column(Integer, primary_key=True)

    _post_status = Column(String)

    @property
    def post_status(self):
        return self._post_status


print (A.post_status)
print (A.post_status != 5678)

输出:

$ python test.py
<property object at 0x10165bd08>
True

您要查找的用法类型类似于 hybrid attribute,它是包含 SQLAlchemy 的“常规”python 描述符扩展,它产生与核心 SQL 表达式兼容的类级行为:

from sqlalchemy.ext.hybrid import hybrid_property

class A(Base):
    __tablename__ = 'a'

    id = Column(Integer, primary_key=True)

    _post_status = Column(String)

    @hybrid_property
    def post_status(self):
        return self._post_status

print (A.post_status)
print (A.post_status != 5678)

输出:

$ python test.py
A._post_status
a._post_status != :_post_status_1

请务必仔细阅读混合文档,包括 how to establish the correct SQL expression behavior,在实例和类级别都有效的描述符是一种有点高级的 Python 技术。

【讨论】:

  • 我最终只是针对 _post_status 而不是属性 post_status 进行测试。这听起来像是更好的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-08
  • 1970-01-01
  • 2022-08-24
  • 1970-01-01
相关资源
最近更新 更多