我正在编写一个允许用户在文件中上传数据的应用程序;该应用程序将处理此数据,并将结果通过电子邮件发送给用户。处理可能需要一些时间,所以我想在Python脚本中单独处理它,而不是在视图中等待它完成。 Python脚本和视图不需要通信,因为脚本将从视图写入的文件中获取数据。视图将显示一条消息,例如“感谢您上传数据 - 结果将通过电子邮件发送给您”
在Django中执行此操作的最佳方法是什么?产生一个单独的过程?把东西放在队列上?
非常感谢一些示例代码。感谢。
答案 0 :(得分:17)
最简单的解决方案是编写自定义commands,搜索所有未处理的文件,处理它们,然后通过电子邮件发送给用户。管理命令在Django框架内运行,因此可以访问所有模型,数据库连接等,但您可以从任何地方调用它们,例如crontab。
如果您关心文件上传和处理开始之间的时间范围,您可以使用像Celery这样的框架,它基本上是一个帮助库,用于使用消息队列和运行工作人员监听队列。这将是相当低的延迟,但另一方面,简单性对您来说可能更重要。
我强烈建议不要在视图中启动线程或生成进程,因为线程将在django进程内运行并可能破坏您的Web服务器(取决于您的配置)。子进程将继承您可能不想要的Django进程中的所有内容。最好把这些东西分开。
答案 1 :(得分:4)
我目前有一个具有类似要求的项目(只是更复杂的^^)。
永远不要从Django视图中生成子进程或线程。你无法控制Django进程,它可能会在任务结束前被杀死,暂停等。它由Web服务器控制(例如,通过WSGI的apache)。
我要做的是一个外部脚本,它将在一个单独的进程中运行。我认为你有两个解决方案:
现在您可能需要访问Django模型中的信息,最后通过电子邮件发送给用户。这里有几个解决方案:
我会去外部进程并导入模块或POST请求。这样它更灵活。例如,您可以使用多处理模块同时处理多个文件(从而有效地使用多核机器)。
基本工作流程将是:
我的项目包含真正需要CPU的处理。我目前使用一个外部进程,为一个工作进程池提供处理作业(这基本上就是Celery可以为你做的),并通过POST请求将进度和结果报告给Django应用程序。它工作得很好并且相对可扩展,但我很快就会改变它以在集群上使用Celery。
答案 2 :(得分:3)
您可以生成thread来进行处理。这与Django没什么关系;视图函数需要启动工作线程,就是这样。
如果您真的想要一个单独的流程,则需要subprocess模块。但是,您真的需要重定向标准I / O还是允许外部过程控制?
示例:
from threading import Thread
from MySlowThing import SlowProcessingFunction # or whatever you call it
# ...
Thread(target=SlowProcessingFunction, args=(), kwargs={}).start()
我还没有完成一个我不想跟踪线程进度的程序,所以我不知道这是否有效而没有在某处存储Thread
对象。如果你需要这样做,那很简单:
allThreads = []
# ...
global allThreads
thread = Thread(target=SlowProcessingFunction, args=(), kwargs={})
thread.start()
allThreads.append(thread)
当thread.is_alive()
返回False
时,您可以从列表中删除帖子:
def cull_threads():
global allThreads
allThreads = [thread for thread in allThreads if thread.is_alive()]
答案 3 :(得分:1)
您可以使用多处理。 http://docs.python.org/library/multiprocessing.html
基本上,
def _pony_express(objs, action, user, foo=None):
# unleash the beasts
def bulk_action(request, t):
...
objs = model.objects.filter(pk__in=pks)
if request.method == 'POST':
objs.update(is_processing=True)
from multiprocessing import Process
p = Process(target=_pony_express, args=(objs, action, request.user), kwargs={'foo': foo})
p.start()
return HttpResponseRedirect(next_url)
context = {'t': t, 'action': action, 'objs': objs, 'model': model}
return render_to_response(...)