执行前将 Celery 的 Django Queryset 序列化为 JSON
Serialize Django Queryset to JSON for Celery before execution
我从外部库中获得了一个查询集,并且-由于查询集是惰性的-我想将其序列化为JSON在它被访问之前,并且因此在数据库中执行,因此它可以在异步 Celery 任务中执行。
我的问题是,有没有一种方法可以将查询集的基本元素表示为 JSON,这样我就不必使用 Pickle?
我知道我可以使用 queryset.query
获得原始查询,但由于我必须在另一端执行原始查询,所以我不太喜欢这个想法。
详情
这里的具体用例是在任务中评估查询集,然后可以将结果导出到文件并转储到服务器上供以后访问。
结果集可能非常大,因此作为标准 request/response 周期的一部分进行导出通常会超时。
该请求是从 Django 管理更改视图列表过滤器生成的。我试过返回列表过滤器代码,但它似乎只是生成过滤器对象 - 不适合 JSON 序列化。
我可以查看列表过滤器查询字符串并重新评估查询集过滤器中的所有键,但许多过滤器 SimpleListFilter
类 也只是 return 过滤对象,并且不在 URL 中显示真正的查询集过滤器。我需要为这些过滤器重新创建逻辑,只是为了获得可以 JSON 序列化的 kwarg 风格的过滤器。
您不必序列化 QuerySet
即可在 Celery 任务中访问它。您可以使用 Django 模型并在任务本身中创建 QuerySet
。如果有一些特定的方式创建 QuerySet
,您可以将 QuerySet
创建代码放在一个公共函数或 class 中,并在 Web 应用程序代码和 Celery 任务中导入此共享代码.
没有理由避免在 Celery 任务中使用 Django ORM。
Django 1.11 和 Celery 3.0 更新
遗憾的是,这不再有效。如果有人知道如何在执行前正确序列化查询集或其各种过滤器和属性,请告诉我。
原回答
事实证明,my_queryset.query
是在执行前序列化 QuerySet 的正确且安全的方法。
If you only want to pickle the necessary information to recreate the QuerySet from the database at a later time, pickle the query attribute of the QuerySet. You can then recreate the original QuerySet (without any results loaded) using some code like this:
import pickle
query = pickle.loads(s) # Assuming 's' is the pickled string.
qs = MyModel.objects.all()
qs.query = query # Restore the original 'query'.
The query attribute is an opaque object. It represents the internals of the query construction and is not part of the public API. However, it is safe (and fully supported) to pickle and unpickle the attribute’s contents as described here.
https://docs.djangoproject.com/en/1.11/ref/models/querysets/#pickling-querysets
这里讨论的是使用 pickle,但它当然也适用于 JSON 序列化程序。
# admin.py
...
export_data.delay(queryset.query)
...
# tasks.py
@task(serializer='json')
def export_data(query):
qs = MyModel.objects.all()
qs.query = query
...
您需要decode
和encode
查询。使用 Django 2.1 和 Celery 4.2 使用此代码:
query_dump = pickle.dumps(queryset.query).decode('latin1')
query = pickle.loads(query_dump.encode('latin1'))
在撰写本文时,使用 Python 3.7+ 和 Django 2.2 pickle
dumps/loads 似乎可行。
它最后只缺少一个 queryset.update()
。
我试过类似的方法:
# packing
queryset = MyModel.objects.filter(...)
serialized_queryset = pickle.dumps(queryset.query)
# unpacking
restored = MyModel.objects.all()
restored.query = pickle.loads(serialized_queryset)
restored.update()
而且效果很好。尚未在 celery 任务中进行测试,但我希望它能正常工作。
我从外部库中获得了一个查询集,并且-由于查询集是惰性的-我想将其序列化为JSON在它被访问之前,并且因此在数据库中执行,因此它可以在异步 Celery 任务中执行。
我的问题是,有没有一种方法可以将查询集的基本元素表示为 JSON,这样我就不必使用 Pickle?
我知道我可以使用 queryset.query
获得原始查询,但由于我必须在另一端执行原始查询,所以我不太喜欢这个想法。
详情
这里的具体用例是在任务中评估查询集,然后可以将结果导出到文件并转储到服务器上供以后访问。
结果集可能非常大,因此作为标准 request/response 周期的一部分进行导出通常会超时。
该请求是从 Django 管理更改视图列表过滤器生成的。我试过返回列表过滤器代码,但它似乎只是生成过滤器对象 - 不适合 JSON 序列化。
我可以查看列表过滤器查询字符串并重新评估查询集过滤器中的所有键,但许多过滤器 SimpleListFilter
类 也只是 return 过滤对象,并且不在 URL 中显示真正的查询集过滤器。我需要为这些过滤器重新创建逻辑,只是为了获得可以 JSON 序列化的 kwarg 风格的过滤器。
您不必序列化 QuerySet
即可在 Celery 任务中访问它。您可以使用 Django 模型并在任务本身中创建 QuerySet
。如果有一些特定的方式创建 QuerySet
,您可以将 QuerySet
创建代码放在一个公共函数或 class 中,并在 Web 应用程序代码和 Celery 任务中导入此共享代码.
没有理由避免在 Celery 任务中使用 Django ORM。
Django 1.11 和 Celery 3.0 更新
遗憾的是,这不再有效。如果有人知道如何在执行前正确序列化查询集或其各种过滤器和属性,请告诉我。
原回答
事实证明,my_queryset.query
是在执行前序列化 QuerySet 的正确且安全的方法。
If you only want to pickle the necessary information to recreate the QuerySet from the database at a later time, pickle the query attribute of the QuerySet. You can then recreate the original QuerySet (without any results loaded) using some code like this:
import pickle
query = pickle.loads(s) # Assuming 's' is the pickled string.
qs = MyModel.objects.all()
qs.query = query # Restore the original 'query'.
The query attribute is an opaque object. It represents the internals of the query construction and is not part of the public API. However, it is safe (and fully supported) to pickle and unpickle the attribute’s contents as described here.
https://docs.djangoproject.com/en/1.11/ref/models/querysets/#pickling-querysets
这里讨论的是使用 pickle,但它当然也适用于 JSON 序列化程序。
# admin.py
...
export_data.delay(queryset.query)
...
# tasks.py
@task(serializer='json')
def export_data(query):
qs = MyModel.objects.all()
qs.query = query
...
您需要decode
和encode
查询。使用 Django 2.1 和 Celery 4.2 使用此代码:
query_dump = pickle.dumps(queryset.query).decode('latin1')
query = pickle.loads(query_dump.encode('latin1'))
在撰写本文时,使用 Python 3.7+ 和 Django 2.2 pickle
dumps/loads 似乎可行。
它最后只缺少一个 queryset.update()
。
我试过类似的方法:
# packing
queryset = MyModel.objects.filter(...)
serialized_queryset = pickle.dumps(queryset.query)
# unpacking
restored = MyModel.objects.all()
restored.query = pickle.loads(serialized_queryset)
restored.update()
而且效果很好。尚未在 celery 任务中进行测试,但我希望它能正常工作。