我有一个可能很大的查询集,我不想加载到内存中。它是一个自定义的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)
答案 0 :(得分:3)
为什么光标在我的生成器功能中关闭?
因为您使用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()
循环完成。