Python SQLAlchemy-为复杂混合属性创建表达式

Python SQLAlchemy-为复杂混合属性创建表达式,python,sqlalchemy,Python,Sqlalchemy,我在MyModel中有这样一个混合属性: @hybrid_property def state(self): states = [dummy.state_id for dummy in self.dummies.all()] if all(state == "DUMMY" for state in states): return State.query.get("DUMMY").text if all((state

我在
MyModel
中有这样一个
混合属性

@hybrid_property
def state(self):
    states = [dummy.state_id for dummy in self.dummies.all()]
    if all(state == "DUMMY" for state in states):
        return State.query.get("DUMMY").text
    if all((state == "FAKE" or state == "DUMMY") for state in states):
        return State.query.get("FAKE").text
    return State.query.get("INVALID").text
我想在我的资源中查询它,如下所示:

valid_text = State.query.get("FAKE").text
return data_layer.model.query.filter_by(state=valid_text) # Where data_layer.model is MyModel
但我得到一个空数组。只需执行
data\u layer.model.query.all()
即可获取数据,从而使逻辑正常工作

我知道我可能需要为我的属性创建一个
表达式
,但我发现的每个示例都是用于更简单的用例

我试过这个:

@state.expression
def state(cls):
    states = [dummy.state_id for dummy in self.dummies.all()]
    all_dummies = all(state == "DUMMY" for state in states)
    all_fakes_or_dummies = all(
        (state == "FAKE" or state == "DUMMY") for state in states
    )
    dummy_text = State.query.get("DUMMY").text
    fake_text = State.query.get("FAKE").text
    invalid_text = State.query.get("INVALID").text

    return case(
        [
            (
                all_dummies,
                dummy_text,
            ),
            (
                all_fakes_or_dummies,
                fake_text,
            ),
        ],
        else_=invalid_text,
    )
但是我的资源现在返回
sqlalchemy.exc.ArgumentError:不明确的文本:False。使用“text()”函数表示SQL表达式文字,或使用“literal()”表示绑定值。


我想知道如何正确实现这个python逻辑以兼容SQLAlchemy,我想这一定是问题所在。另外,我想知道在混合属性中创建如此复杂的逻辑是否是一种好的做法。

在您的情况下,基于
的查询构建应该会提供您想要的结果,同时生成易于阅读的查询

正如您所说,我试图将检查分解为一些独立的块,结果如下:

class MyModel(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)

    # ...

    # Hybrid Properties
    @hybrid_property
    def has_only_dummies(self):
        # this is not optimal as need to reiterate over all objects
        states = [dummy.state_id for dummy in self.dummies.all()]
        return all(state == "DUMMY" for state in states)

    @hybrid_property
    def has_only_dummies_or_fake(self):
        # this is not optimal as need to reiterate over all objects
        states = [dummy.state_id for dummy in self.dummies.all()]
        return all(state in ("DUMMY", "FAKE") for state in states)

    @hybrid_property
    def state(self):
        # could reuse other hybrid properties here, but it is not efficient at all
        res = None
        states = [dummy.state_id for dummy in self.dummies.all()]
        if all(state == "DUMMY" for state in states):
            res = "DUMMY"
        elif all((state == "FAKE" or state == "DUMMY") for state in states):
            res = "FAKE"
        else:
            res = "INVALID"
        return res


    # Hybrid Expressions
    @has_only_dummies.expression
    def has_only_dummies(cls):
        subq = (
            exists()
            .where(Dummy.model_id == cls.id)
            .where(~Dummy.state_id.in_(["DUMMY"]))
        ).correlate(cls)

        return select([case([(subq, False)], else_=True)]).label("only_dum")

    @has_only_dummies_or_fake.expression
    def has_only_dummies_or_fake(cls):
        subq = (
            exists()
            .where(Dummy.model_id == cls.id)
            .where(~Dummy.state_id.in_(["DUMMY", "FAKE"]))
        ).correlate(cls)

        return select([case([(subq, False)], else_=True)]).label("only_dum_or_fak")

    @state.expression
    def state(cls):
        return db.case(
            [
                (cls.has_only_dummies, "DUMMY"),
                (cls.has_only_dummies_or_fake, "FAKE"),
            ],
            else_="INVALID",
        )
在这种情况下,您可以构建如下查询,包括筛选:

q = session.query(MyModel, MyModel.has_only_dummies, MyModel.has_only_dummies_or_fake, MyModel.state)
q = session.query(MyModel, MyModel.state)
q = session.query(MyModel).filter(MyModel.state != "INVALID")
MyModel.state
并不是您想要的(它是
state\u id
而不是
text
),但是获取文本是另一个步骤,如果您真的需要它,它很容易实现