简体   繁体   English

SQLAlchemy:多对多关系查询中的过滤计数

[英]SQLAlchemy: filtering count in many-to-many relationship query

In my Flask app, there is a many-to-many relationship between Documents and Tokens: 在我的Flask应用程序中,Documents和Tokens之间存在多对多关系:

DocTokens = db.Table(
    'DocTokens',
    db.Column('token_id', db.Integer, db.ForeignKey('Token.id')),
    db.Column('document_id', db.Integer, db.ForeignKey('Document.id')),
    )

class Token(db.Model):
    __tablename__ = 'Token'
    id = db.Column(db.Integer, primary_key=True)
    ...
    is_gold = db.Column(db.Boolean, default=None)

class Document(db.Model):
    __tablename__ = 'Document'
    id = db.Column(db.Integer, primary_key=True)
    ...
    tokens = db.relationship(
        'Token',
        secondary=DocTokens,
        backred=db.backref('documents', lazy='dynamic'),
        )

I'd like to construct a Document query , ordered (descendingly) by the number of related Tokens where Token.is_gold is None . 我想构建一个Document查询 ,按Token.is_gold为None的相关Tokens的数量排序(降序)。

So far, I've figured out how to order Documents by the number of related Tokens: 到目前为止,我已经找到了如何通过相关代币的数量来订购文档:

db.session.query(
    Document,
    func.count(DocTokens.c.token_id).label('total')
    ).join(DocTokens).group_by(Document).order_by('total DESC')

But, I can't seem to make that count include only Tokens where Token.is_gold is None. 但是,我似乎无法将该数量仅包括Token.is_gold为None的Tokens。 Here is one of many failed attempts: 以下是许多尝试失败的尝试之一:

db.session.query(
    Document,
    func.count(DocTokens.c.token_id)
    .filter(Token.is_gold.is_(None)).label('total')
    ).join(DocTokens).group_by(Document).order_by('total DESC')

It threw the following error: 它引发了以下错误:

AttributeError: Neither 'count' object nor 'Comparator' object has an attribute 'filter'

Here are some of the StackOverflow solutions I've tried to model (incl. solutions involving subqueries and hybrid properties): 以下是我尝试建模的一些StackOverflow解决方案(包括涉及子查询和混合属性的解决方案):

I'm fairly new to SQL/SQLAlchemy... Any help is greatly appreciated! 我是SQL / SQLAlchemy的新手...非常感谢任何帮助!

  • The label should be applied to func.count(DocTokens.c.token_id) , and not the filter object. label应该应用于func.count(DocTokens.c.token_id) ,而不是过滤器对象。 You had it right in your first query, but not in the second. 你在第一个查询中没有使用它,但在第二个查询中没有。

  • filter is a method of query object, so you must write it as: filterquery对象的一种方法,因此必须将其写为:

     db.session.query(...).join(...).filter(...).group_by(...).order_by(...) 
  • the filter is applying on a column from Token , so this must be included in the join. 过滤器正在应用于Token的列,因此必须包含在连接中。

Thus, the query written as the following will not give you an error: 因此,写为以下的查询不会给您一个错误:

r = db.session.query(Document,
                  func.count(Token.id).label('total'))\
    .join(DocTokens).join(Token)\
    .filter(Token.is_gold.is_(None))\
    .group_by(Document)\
    .order_by('total DESC')

This will produce the following sql (using sqlite as the backend) 这将产生以下sql(使用sqlite作为后端)

'SELECT "Document".id AS "Document_id", count("DocTokens".token_id) AS total \\nFROM "Token", "Document" JOIN "DocTokens" ON "Document".id = "DocTokens".document_id \\nWHERE "Token".is_gold IS NULL GROUP BY "Document".id ORDER BY total DESC' 'SELECT“Document”.id AS“Document_id”,count(“DocTokens”.token_id)AS total \\ nFROM“Token”,“Document”JOIN“DocTokens”ON“Document”.id =“DocTokens”.document_id \\ nWHERE“令牌“.is_gold IS NULL GROUP BY”文档“.id ORDER BY DESC'

update: if you're not sure what sql will be generated from a query object, you can always examine it with a str , ie 更新:如果你不确定从查询对象生成什么sql,你总是可以用str检查它,即

If I run str(r) in my example query, it prints the sql quoted above. 如果我在我的示例查询中运行str(r) ,它会打印上面引用的sql。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM