由于这个问题似乎很笼统,我相信这是根据我的个人经验制作关于“如何使用 django-background-tasks”的快速备忘单的正确位置。希望我不会是唯一使用它的人:)
环境
安装
我喜欢pipenv 所以:
> cd [my-django-project root directory]
> pipenv install django-background-tasks
现在在 settings.py 中将“background_task”添加到 INSTALLED_APPS:
INSTALLED_APPS = (
# ...
'background_task',
# ...
)
并执行数据库迁移以确保 django-background-tasks 架构就位:
> pipenv shell
(my-django-project) bash-3.2$ python manage.py migrate
创建和注册任务
任何 Python 函数都可以是一个任务,我们只需要应用 @background 注释来注册它:
from background_task import background
@background(schedule=10)
def do_something(s1: str, s1: str) -> None:
"""
Does something that takes a long time
:param p1: first parameter
:param p2: second parameter
:return: None
"""
pass
现在我们可以像往常一样在项目中调用该函数了:
do_something("first parameter", "second parameter")
需要注意的是,调用函数不会实际执行其代码;而是由“django-background-tasks”模块将任务记录存储到数据库中,更准确地说是存储在“background_task”表中。因为这个原因,写一个返回一些东西的任务函数是没有多大用处的,因为无论如何任务都会在稍后的时刻在后台执行,所以函数在被调用时返回的“值”几乎是没有意义的.我看到的返回值的唯一用例是用于测试目的,请参阅下面的测试任务部分。
处理任务
为了实际运行注册任务,我们必须使用以下管理命令:
> python manage.py process_tasks
有关命令选项的说明,请参阅module's documentation。
正如其他用户已经指出的那样,通常将此命令包装在 cron 作业中以确保定期处理任务。在这种情况下,duration 选项可能会很有用:它表示 process_task 命令保持运行的秒数。默认情况下,持续时间为 0,这意味着“永远运行它”,但在我看来这是非常冒险的,因为如果由于某种原因命令崩溃或被中断,您的任务将不再被处理并且可能会经过很长时间你意识到了。
更好的方法是将持续时间设置为明确定义的时间,例如 15 分钟,然后将 cron 作业配置为每 15 分钟运行一次以重新启动处理命令。这样,如果命令崩溃,它将在稍后由 cron 作业重新启动。
测试任务
通过“process_tasks”管理命令测试任务很糟糕,我们应该坚持使用 Python unittest 模块,这也是“Django 方式”。
我当然不会在这篇文章中讨论 unittest,我只想指出,在单元测试期间,您希望以同步方式执行函数,就像正常的Python 函数。其语法如下:
do_something.now("first parameter", "second parameter")
修饰符“now”运行函数并等待它终止。这是我认为返回值有用的唯一用例。有了返回值,您就可以使用 unittest 提供的“assert*”函数的全部功能。
检查任务是否已经在运行
有时您可能不希望同一任务运行多次。例如,我经常使用后台任务来训练机器学习模型,这需要花费大量时间。为了防止我的数据被弄乱,我更愿意确保在前一个训练任务完成之前不能开始对同一模型的另一个训练任务。
为此,我必须在开始新任务之前检查任务是否已经在运行;但是如何唯一标识一个任务呢?对我来说,简单的方法是为任务分配一个“verbose_name”,这可以在计划任务时完成:
do_something("first parameter", "second parameter", verbose_name="my_task_verbose_name")
现在,如果我想检查这个任务是否已经在运行,我可以简单地阅读 background_task 表并验证其中没有具有相同“详细名称”的任务。这可以通过利用“django-background-tasks”本身提供的 Task 模型轻松完成:
from background_task.models import Task
tasks = Task.objects.filter(verbose_name="my_task_verbose_name")
if len(tasks) == 0:
# no task running with this name, go ahead!
pass
else:
# task already running
pass
不用说,我们必须确保分配给我们任务的详细名称是唯一的。
进一步阅读
Django Background Tasks documentation