python - 为什么当我将光标传递给 StreamingHttpResponse 时,我的生成器函数中的光标已关闭?
问题描述
我有一个可能很大的查询集,我不想加载到内存中。这是一个自定义 SQL 语句。
django.http.StreamingHttpResponse
接受一个iterator
(生成器)参数。为了避免将所有内容加载到内存中,我正在使用 Postgres 服务器端游标和fetchmany
(尽管我尚未验证这是否真的有效)。
这是我传递的生成器函数:
def queryset_generator(cursor, chunk_size=CHUNK_SIZE):
while True:
if cursor.closed:
yield "cursor closed!"
break
rows = cursor.fetchmany(chunk_size)
if not rows:
break
yield rows
我测试游标是否关闭,否则当代码之后尝试访问关闭的游标时,psycopg2 会抱怨。
以下是我在视图中传递它的方式(简化 SQL):
with connections['mydb'].cursor() as cursor:
cursor.execute("SELECT * FROM foobar;")
return StreamingHttpResponse(queryset_generator(cursor))
这一直给我
光标关闭!
为什么我的生成器函数中的光标关闭了?如果我在我看来这样做会很好:
with connections['mydb'].cursor() as cursor:
cursor.execute("SELECT * FROM foobar;")
return StreamingHttpResponse(cursor.fetchall())
同样值得注意的是,这在 shell 中运行良好:
cursor = connections['mydb'].cursor()
cursor.execute(...)
for x in StreamingHttpResponse(queryset_generator(cursor))._iterator:
print(x)
解决方案
为什么我的生成器函数中的光标关闭了?
因为您使用以下命令退出上下文管理器return
:
return StreamingHttpResponse(queryset_generator(cursor))
这将退出with
块,触发__exit__
上下文管理器上的方法,并且此方法关闭游标。with
语句或上下文管理器无法知道您只是将对该对象的引用传递给仍然cursor
需要它保持打开状态的其他对象。with
不关心引用,只关心语义块的结尾。
如果您需要在StreamingHttpResponse()
实例完成流式传输数据之前保持光标打开,则不能在return
语句周围使用上下文管理器。
传入一个游标而不在上下文管理器中使用它,并让queryset_generator()
函数负责使用with
:
def queryset_generator(cursor, chunk_size=CHUNK_SIZE):
with cursor:
while True:
if cursor.closed:
yield "cursor closed!"
break
rows = cursor.fetchmany(chunk_size)
if not rows:
break
yield rows
和
cursor = connections['mydb'].cursor()
cursor.execute("SELECT * FROM foobar;")
return StreamingHttpResponse(queryset_generator(cursor))
现在光标保持打开状态,直到while
循环queryset_generator()
完成。
推荐阅读
- composition - 如何使用 Vue Apollo Composible 手动调用查询(例如点击)?
- swift - 在单元格内的标签中以编程方式使用属性文本
- flutter - 如何为 Flutter web 和 mobile 分别添加包?
- c# - c# AutoML 回归训练不会显示 Rsquared 或 MeanAbsoluteError
- colors - Gnuplot 5:曲线之间的颜色渐变阴影
- rascal - Rascal:TrafoFields 语法错误:具体语法片段
- cplex - Cplex Optimization Studio 中保存结果的位置
- azure - 将默认 DNS 域名添加到 azure Web 应用
- java - Selenium JS 执行器失败:“无法在文档上执行 elementsFromPoint”
- python - 来自具有不同长度和键的字典列表的 CSV