【问题标题】:Query additional data with SQLAlchemy ORM使用 SQLAlchemy ORM 查询附加数据
【发布时间】:2015-04-13 21:49:15
【问题描述】:

假设我有一个博客系统的简单模型:

class Blog(db.Model):

    __tablename__ = 'blog'

    id = db.Column(db.BigInteger, primary_key=True)
    posts = db.relationship('Post', backref='blog', lazy='dynamic')

class Post(db.Model):

    __tablename__ = 'post'

    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String())

我使用的是 Flask-SQLAlchemy,所以语法可能看起来有点不同,但映射非常简单。

是否可以在帖子计数旁边获取所有博客?因为现在迭代一个列表会在每次迭代时执行一个额外的查询,并且这并不能真正扩展。在 SQL 中非常简单:

SELECT b.*, COUNT(p)
FROM blog b
    JOIN post p ON(b.id = p.blog_id)
GROUP BY b.id;

但是在 SQLAlchemy 中它是如何完成的呢?我最好有两个选择:

  • 在查询时指定
  • 在模型本身中创建一个“虚拟”属性以始终使用它(例如,Hibernate 将其称为 formulas

我认为我缺少正确的术语,因为我在 Google 上找不到任何东西。

【问题讨论】:

    标签: python sqlalchemy flask-sqlalchemy


    【解决方案1】:

    见下文,代码应该可以回答您的两个问题。第二部分的答案使用Hybrid Extension

    class Blog(db.Model):
        __tablename__ = 'blog'
    
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String)
        posts = db.relationship('Post', backref='blog', lazy='dynamic')
    
        @hybrid_property
        def num_posts(self):
            # return len(self.posts)  # use if relationship is *not* dynamic
            return self.posts.count()
    
        @num_posts.expression
        def _num_posts_expression(cls):
            return (db.select([db.func.count(Post.id)])
                    .where(Post.post_id == cls.id)
                    .label("num_posts")
                    )
    
    
    class Post(db.Model):
        __tablename__ = 'post'
    
        id = db.Column(db.Integer, primary_key=True)
        post_id = db.Column(db.ForeignKey(Blog.id))
        title = db.Column(db.String())
    
    
    def test():
        with app.app_context():
            db.drop_all()
            db.create_all()
    
            def create_test_data():
                blogs = [
                    Blog(name="empty",),
                    Blog(name="geo",
                         posts=[
                             Post(title='west'),
                             Post(title='east'),
                             Post(title='north'),
                             Post(title='south'),
                         ]),
                    Blog(name="food",
                         posts=[
                             Post(title='sour'),
                             Post(title='sweet'),
                         ]),
                ]
                db.session.add_all(blogs)
                db.session.commit()
    
            create_test_data()
    
    
            # simple query
            q = (db.session.query(Blog, db.func.count(Post.id).label("num_posts"))
                 .outerjoin(Post, Blog.posts)
                 .group_by(Blog.id)
                 )
            for b, num_posts in q:
                print(b, num_posts)
            print("-"*80)
    
    
            # Using hybrid_attribute
            # num_posts is fetched later
            q = (db.session.query(Blog))
            for b in q:
                print(b)
                print(b.num_posts)
            print("-"*80)
    
            # num_posts is fetched with the query
            q = (db.session.query(Blog, Blog.num_posts))
            for b in q:
                print(b)
                print(b.num_posts)
            print("-"*80)
    

    【讨论】:

    • 看起来很有希望,我晚上试试。您是否碰巧知道是否可以将计数分配给Blog 的属性而不是返回元组(或其他)?我知道我可以循环自己做,但也许 SQLAlchemy 可以自动做?
    • # simple query 是您问题的第一部分。其余部分使用hybrid_property,这正是Blog 类的一个属性,从代码中可以看出。
    • 你是对的,它适用于混合属性。但它也适用于简单查询吗?
    猜你喜欢
    • 2021-09-21
    • 2021-09-22
    • 2017-11-28
    • 2019-05-18
    • 1970-01-01
    • 2021-09-05
    • 2021-02-06
    • 1970-01-01
    • 2012-03-24
    相关资源
    最近更新 更多