【问题标题】:Flask SQLAlchemy query with concatenated columns带有连接列的 Flask SQLAlchemy 查询
【发布时间】:2019-09-08 14:05:35
【问题描述】:

我有这样的模型:

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    first_name = db.Column(db.String(64), index=True)
    last_name = db.Column(db.String(64), index=True)
    
    def full_name(self):
        return '%s %s' % (self.first_name, self.last_name)

我想在查询中获取full_name 方法,我试试看:

user = db.session.query(User.full_name()).all()

但我收到此错误消息:

TypeError: full_name() missing 1 required positional argument: 'self'

然后我尝试调用不带括号的函数:

user = db.session.query(User.full_name).all()

然后我收到此错误消息:

sqlalchemy.exc.InvalidRequestError: SQL expression, column, or mapped entity expected - got '<function User.full_name at 0x7f265960aae8>'

那么,在 User 模型中查询full_name() 方法的正确方法是什么..?

【问题讨论】:

    标签: python sqlalchemy flask-sqlalchemy


    【解决方案1】:

    有很多方法可以做到这一点。首先,hybrid attribute,一个在实例和类上定义full_name 的属性(用于查询)。

    这个例子是纯 SQLAlchemy,但是混合属性在 Flask-SQLAlchemy 中应该是一样的。

    import sqlalchemy as sa
    from sqlalchemy.ext import hybrid
    
    class User(Base):
        __tablename__ = 'users' 
                                                                                                                                                                            
        id = sa.Column(sa.Integer, primary_key=True)
        first_name = sa.Column(sa.String)
        last_name = sa.Column(sa.String)
    
        @hybrid.hybrid_property
        def full_name(self):  
            return self.first_name + ' ' + self.last_name
    
    users = session.query(User).filter_by(full_name='Joan Doe').all()
    

    感谢Ilja for pointing out 在这种情况下可以使用单一方法完成。这是因为 SQLAlchemy 将 + operator 映射到数据库的 CONCAT 函数。如果不是这种情况,则需要使用hybrid.expression 装饰的附加属性来实现类级别的行为。

    SQLAlchemy 根据需要将必要的表达式插入到 SQL 语句中,例如:

    SELECT users.id, users.first_name, users.last_name 
    FROM users 
    WHERE users.first_name || ? || users.last_name = ?
    

    实现此目的的另一种方法是定义column_property

    from sqlalchemy import orm
    
    class User(Base):
        ...
        full_name = orm.column_property(first_name + " " + last_name)
    

    这会在模型上的所有个查询中插入一个表达式,所以

    query = sa.select(User)
    

    将生成此 SQL:

    SELECT users.first_name || ? || users.last_name AS anon_1,
           users.id, 
           users.first_name, 
           users.last_name 
    FROM users
    

    最后还有computed columns。这些在概念上类似于列属性,但在数据库端实现(假设数据库支持所需的语法)。数据库可能会或可能不会物理存储计算值;可以通过将 persisted 布尔关键字参数传递给 Computed 来调节这种行为。

    class User(Base):
        ...
        full_name = sa.Column(sa.String, sa.Computed(first_name + ' ' + last_name))
    

    【讨论】:

    • 在这种情况下,单个方法也可以:)(用于表达的方法对两者都适用,我认为它甚至是文档中的示例)。
    【解决方案2】:

    您可以使用@classmethod

    class User(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        first_name = db.Column(db.String(64), index=True)
        last_name = db.Column(db.String(64), index=True)
    
        @classmethod
        def full_name_filter(cls, fname, lname):
            return (cls.first_name == fname, cls.last_name == lname)
    

    然后

    user = db.session.query(User).filter(*User.full_name_filter("first", "last")).all()
    

    【讨论】:

    • 您好,感谢您参与回答我的问题,但我仍然遇到同样的错误:TypeError: full_name() missing 1 required positional argument: 'self' 更多信息,我在不同的模块中查询它,而不是在课程本身中。跨度>
    猜你喜欢
    • 2021-08-18
    • 2019-12-13
    • 2015-03-10
    • 1970-01-01
    • 2019-07-22
    • 1970-01-01
    • 2016-05-08
    • 2017-06-08
    • 2020-03-31
    相关资源
    最近更新 更多