SQLAlchemy选择所有多对多关系对象作为列表

时间:2013-01-16 04:07:03

标签: python sqlalchemy flask flask-sqlalchemy

鉴于这种多对多的关系:

tagmap = db.Table('tagmap', db.Model.metadata,
    db.Column('post_id', db.Integer, db.ForeignKey('posts.id'),
    db.Column('tag_id', db.Integer, db.ForeignKey('tags.id'),)

class Post(db.Model):
    __tablename__ = 'posts'
    id    = db.Column(db.Integer, primary_key=True)
    text  = db.Column(db.Text)
    tags  = db.relationship('Tag', secondary=tagmap, backref='posts')

class Tag(db.Model):
    __tablename__ = 'tags'
    id    = db.Column(db.Integer, primary_key=True)
    name  = db.Column(db.String)

如何构建查询以选择所有帖子,包括相关标签对象的可迭代列表?

所需的查询结果结构:

[ <Post 1, "Blah",      [<Tag "Goober">, <Tag "Mexico">]>,
  <Post 2, "Daft Punk", [<Tag "French">]>,
  <Post 3, "Slurpee",   [<Tag "Diabetes">, <Tag "Tasty">, <Tag "Sugary">]>,
  <Post 4, "Lasers",    []> ]

Jinja模板中所需的结果使用示例:

{% for post in posts %}
    {{ post.text }}
    <hr>
    {% for tag in post.tags %}
        {{ tag.name }}, 
    {% endfor %}
{% endfor %}

修改

尝试扩展查询时,标记将不再包含在内。请参阅下面的查询失败:

Post.query.join(User)\
    .options(db.joinedload(Post.tags))\
    .order_by(Post.create_date.desc()).limit(5)\
    .values(User.name.label('author'),
            Post.create_date,
            Post.text,)

编辑2

所以这个查询效果很好:

posts = db.session.query(Post, User.name.label('author')).join(User)\
            .options(db.joinedload(Post.tags))\
            .order_by(Post.created.desc()).limit(5)

但如果我想选择Post I中的列,请选择:

# I only need the post text, nothing else
posts = db.session.query(Post.text, User.name.label('author')).join(User)\
            .options(db.joinedload(Post.tags))\
            .order_by(Post.created.desc()).limit(5)

它开始快速崩溃:

ArgumentError: Query has only expression-based entities - can't find property named 'tags'.

所以我尝试重新添加标签:

# Yes, I know this is wrong
posts = db.session.query(Post.text, Post.tags, User.name.label('author'))\
            .join(User)\               
            .options(db.joinedload(Post.tags))\
            .order_by(Post.created.desc()).limit(5)

仍然失败。

因此,如果你只想要父对象(Post)中的特定列,你在表达式中说“我还想要标签”吗?

现在并不是那么重要,但我认为知道它会很有用。或者甚至知道这是不可能的。

2 个答案:

答案 0 :(得分:7)

我假设你想使用渴望加载来建立关系。您可以通过在lazy'joined'的关系中设置'subquery'关键字参数来实现这一目标:

class Post(db.Model):
    # ...
    tags = db.relationship('Tag', secondary=tagmap, backref='posts',
                           lazy='joined')    # Or lazy='subquery'

或者您可以按查询设置:

q = Post.query.options(db.joinedload(Post.tags)).all()
# Or db.subqueryload(Post.tags)

有关详细信息,请参阅ORM教程的Eager Loading部分和Relationship Loading Techniques

答案 1 :(得分:0)

SQLAlchemy在您访问它时会自动加载post.tags属性,因此您只需要加载所需的所有帖子。

db.Column('tag_id', db.Integer, db.ForeignKey('quotes.id'),)

quotes.id是错字吗?