过滤列表中的每个项目以检查其是否在ID的列表中

时间:2018-08-27 10:12:41

标签: python flask sqlalchemy flask-sqlalchemy

我只想获得结果,其中对于每个游戏类别而言,该类别都位于ID列表中,因此无法执行以下操作。 Game.categories是hybrid_property,可为我提供游戏的类别。

这是我正在执行的查询,希望获得我指定的ID列表中至少具有一个类别的所有游戏的列表。

<appSettings file="C:\Program Files\Common\ServicesPlatform.config">
    <add key="someKey" value="someValue"/>
</appSettings>

我的models.py是这个。请注意,每个游戏的类别取决于用户定义类别的数量。

games = Game.query \
  .filter([category.id for category in Game.categories].in_([1, 2, 3])) \
  .paginate(page, current_app.config['POSTS_PER_PAGE'], False)

我收到此错误

class CategoryUpvote(db.Model):
  id = db.Column(db.Integer, primary_key=True)
  user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
  category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
  game_id = db.Column(db.Integer, db.ForeignKey('game.id'))

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

class Game(db.Model):
  id = db.Column(db.Integer, primary_key=True)
  title = db.Column(db.String(255), index=True, unique=True)

  category_upvotes = db.relationship('CategoryUpvote', backref='category', lazy='dynamic', cascade="save-update, merge, delete")

  def add_upvote(self, category, user):
    if not self.is_upvoted(category, user):
      upvote = CategoryUpvote(category_id=category.id, game_id=self.id, user_id=user.id)
      db.session.add(upvote)
    else:
      upvote = self.category_upvotes.filter(CategoryUpvote.category_id == category.id, CategoryUpvote.user_id == user.id, CategoryUpvote.game_id == self.id).first()
      db.session.delete(upvote)

  def is_upvoted(self, category, user):
    return self.category_upvotes.filter(CategoryUpvote.category_id == category.id, CategoryUpvote.user_id == user.id, CategoryUpvote.game_id == self.id).count() > 0

  @hybrid_property
  def categories(self):
    return Category.query \
      .filter(CategoryUpvote.game_id == self.id, CategoryUpvote.category_id == Category.id) \
      .group_by(Category.id) \
      .order_by(db.func.count(CategoryUpvote.game_id).desc()) \
      .limit(5) \
      .all()

如何以某种方式为游戏的每个类别运行过滤器?例如

'list' object has no attribute 'in_'

如果Game将每个类别都作为外键,我也可以这样做

.filter(Game.categories[0].in_([1, 2, 3]), Game.categories[1].in_([1, 2, 3]), Game.categories[2].in_([1, 2, 3])) \

如何使用hybrid_property做类似的事情?

2 个答案:

答案 0 :(得分:3)

属性Game.categories尽管表示为hybrid_property,但在SQL上下文中不起作用。当用于在类级别创建SQL expression objects时,它仍然对数据库执行实际查询,而是返回Category对象的列表。您的大多数错误都是由于这一事实造成的。

此外,它实际上在GameCategoryUpvote之间执行联接(使用SQL-92之前的样式),从 all 游戏中返回5个投票最高的类别。如果返回Query而不显示在列表中,它将使该属性在一般情况下更加有用:

class Game(db.Model):

  @hybrid_property
  def categories(self):
    return Category.query \
      .join(CategoryUpvote) \
      .filter(CategoryUpvote.game_id == self.id) \
      .group_by(Category.id) \
      .order_by(db.func.count(CategoryUpvote.game_id).desc()) \
      .limit(5)

然后您将使用{p>来访问Game实例的类别列表

game.categories.all()

或直接直接对其进行迭代。通过hybrid属性返回查询,您现在可以使用它来生成相关的子查询。然后,您可以尝试根据ID列表过滤类别,以谓词“ id所标识的游戏的给定ID和类别ID之差为空”,换句话说,所有给定ID为包含在游戏的前5个类别ID集中:

# A portable way to build a (derived) table of ids. Use VALUES
# instead, if supported.
ids = union(*[select([literal(i).label('id')]) for i in [1, 2, 3]])

Game.query.filter(~exists(
    ids.select()
        .except_(Game.categories
            .correlate(Game)
            .with_entities(Category.id)
            .subquery()
            .select())))

请注意

  

我只想获得结果,其中每个游戏类别的类别都在ID的列表中

  

这是我正在做的查询,希望获得我指定的ID列表中至少具有一个类别的所有游戏的列表

并非同一件事,尽管严格的解释(列表中的每个类别)将要求使用对称差异,但上面的示例是牢记前者的。

要获取后者,您可以使用谓词“ id所标识的游戏的给定ID与类别ID之间的交集为空”进行查询,因此您需要交换except_()intersect()并删除exists()前面的NOT运算符~ –因为如果某些内容不为空,则存在一行或多行。

答案 1 :(得分:1)

面临的问题是因为您试图使用生成器来运行查询

category.id.in_([1,2,3]) for category in Game.categories

举一个小例子。当您运行以下代码时,您将在列表中获得0到5的期望输出。

>>> print([a for a in range(5)])
[0, 1, 2, 3, 4]

现在,如果您确实删除了方括号,那么

>>> print(a for a in range(5))
<generator object <genexpr> at 0x102829f10>

您可以看到输出是一个生成器表达式。您的代码也是如此,因为您没有评估python会将其作为生成器对象发送

我没有sqlalchemy的经验,但是我觉得您正在尝试实现的目标可以做到这一点:

for category in Game.categories
    result.append(category.id.in_([1, 2, 3]))

games = Game.query \
  .filter(Game.id.in_(result)) \
  .paginate(page, current_app.config['POSTS_PER_PAGE'], False)