使用row_number()删除SQLAlchemy查询



我正在寻找一个合适的方法来使query.delete()使用row_number ()函数。

下面是我当前的解决方案:

subquery = session.query(Foo,
func.row_number().over(order_by=desc(Foo.bar)).label("row_number")).subquery()
subquery = session.query(subquery).filter(subquery.c.row_number > 3)
subquery = subquery.from_self(Foo.id)
query = session.query(Foo).filter(
Foo.id.in_(subquery)
)

我不喜欢需要显式地检查Foo。Id列表中的Id。我需要比较所有对象的列没有硬编码。仕:

subquery = session.query(Foo,
func.row_number().over(order_by=desc(Foo.bar)).label("row_number")).subquery()
subquery = session.query(subquery).filter((subquery.c.row_number > 3))
subquery = subquery.from_self(Foo)
query = session.query(Foo).filter(
Foo.in_(subquery)
)

当然这行不通。

如果有人提出解决办法,我将不胜感激。

经过一段时间的调查,我明白了以下几点:

  1. 您需要为过滤指定精确的模型字段。我创建了一个方法_get_reference_attrs如果没有主键,则使用主键字段或所有字段。
  2. 问题代码使用了3个嵌套的选择查询(不包括删除查询),但只有2个是必需的。为此,您需要正确地收集您在上部选择(attrs_subquery)中选择的内容。)

这就是我的解决方案:

from sqlalchemy import func, desc, tuple_, inspect

def _get_reference_attrs(model):
"""
Collecting attributes of model that will be used in delete query
By default the primary key columns are used. If no primary key - all columns are used.
"""
mapper = inspect(model)
primary_key_names = [primary_key.key for primary_key in mapper.primary_key]
if primary_key_names:
attrs = [attr for attr in mapper.attrs if attr.key in primary_key_names]
else:
attrs = mapper.attrs
return attrs

attrs = _get_reference_attrs(Foo)
subquery = session.query(*attrs,
func.row_number().over(order_by=desc(Foo.bar)).label("row_number")).subquery()
attrs_subquery = [getattr(subquery.c, attr.key) for attr in attrs]
subquery = session.query(*attrs_subquery).select_from(subquery).filter(subquery.c.row_number > 1)
query = session.query(Foo).filter(
tuple_(*attrs).in_(subquery)
)

最新更新