django_apscheduler真实完整例子

整体步骤:

  1. 安装django-apscheduler

  2. 添加到INSTALLED_APPS

  3. 运行迁移,创建数据库表

  4. 创建任务函数

  5. 在apps.py中启动调度器,避免多实例重复运行

  6. 配置调度器,添加定时任务

  7. 运行服务器并测试

需要验证每个步骤是否正确,特别是调度器的启动位置,是否在Django应用加载时正确初始化,以及任务是否被正确调度。同时提醒用户注意可能的问题,比如时区设置,任务执行时间等。以下是使用 django_apscheduler 的完整示例,涵盖安装、配置、任务定义、调度及运行步骤:

1. 环境准备

安装包

bash

python 复制代码
pip install django-apscheduler

2. Django 项目配置

修改 settings.py

python

python 复制代码
INSTALLED_APPS = [
    # ...
    'django_apscheduler',  # 添加此应用
]

# 时区配置(可选,但建议明确)
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = True
创建数据库表(迁移)

bash

python 复制代码
python manage.py migrate

3. 定义定时任务

创建任务函数 jobs.py

python

python 复制代码
# myapp/jobs.py
from django_apscheduler.jobstores import DjangoJobStore
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
from django.conf import settings

def my_scheduled_job():
    """示例任务:每分钟打印日志"""
    print("[APScheduler] 定时任务执行成功!")


def start_scheduler():
    """启动调度器"""
    scheduler = BackgroundScheduler(timezone=settings.TIME_ZONE)
    scheduler.add_jobstore(DjangoJobStore(), "default")

    # 添加任务(每分钟执行一次)
    scheduler.add_job(
        my_scheduled_job,
        trigger=CronTrigger(second="0"),  # 每分钟的第0秒触发
        id="my_job",
        max_instances=1,
        replace_existing=True,
    )

    # 启动调度器
    try:
        scheduler.start()
    except KeyboardInterrupt:
        scheduler.shutdown()

4. 集成到 Django 应用

修改 apps.py(确保调度器只启动一次)

python

python 复制代码
# myapp/apps.py
from django.apps import AppConfig

class MyAppConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'myapp'

    def ready(self):
        """Django 启动时自动初始化调度器"""
        if not settings.DEBUG:  # 生产环境启动,或根据需求调整
            from .jobs import start_scheduler
            start_scheduler()
修改 __init__.py

python

python 复制代码
# myapp/__init__.py
default_app_config = 'myapp.apps.MyAppConfig'

5. 运行与验证

启动 Django 服务

bash

python 复制代码
python manage.py runserver
查看日志输出

plaintext

复制代码
[APScheduler] 定时任务执行成功!  # 每分钟输出一次

6. 高级功能(可选)

在 Django Admin 中管理任务
  • django_apscheduler 自动注册模型到 Admin,访问 /admin 可查看任务状态:
动态添加任务(示例)

python

python 复制代码
# 在代码中动态添加一次性任务
from apscheduler.triggers.date import DateTrigger
from datetime import datetime, timedelta

def add_one_time_job():
    scheduler = BackgroundScheduler()
    scheduler.add_job(
        my_scheduled_job,
        trigger=DateTrigger(run_date=datetime.now() + timedelta(minutes=5)),
        id="one_time_job",
    )

7. 处理多实例冲突

若部署多个 Django 实例,需通过数据库锁避免任务重复执行:

python

复制

复制代码
# 在 jobs.py 的 start_scheduler 中添加锁机制
from django_apscheduler.models import DjangoJobExecution

DjangoJobExecution.objects.select_for_update().filter(job_id="my_job").exists()

完整项目结构

复制代码
myproject/
├── myproject/
│   ├── settings.py
│   └── urls.py
└── myapp/
    ├── __init__.py
    ├── apps.py
    ├── jobs.py
    ├── models.py
    └── admin.py

8.注意事项

  1. 开发环境 :在 DEBUG=True 时可能因代码重载导致调度器多次启动,建议通过 if not settings.DEBUG 控制。

  2. 持久化 :任务状态存储在数据库表 django_apscheduler_djangojobdjango_apscheduler_djangojobexecution 中。

  3. 时区 :确保 TIME_ZONE 配置一致,避免任务时间偏移。

通过以上步骤,你可以在 Django 中快速实现轻量级定时任务调度!

9.多应用APP处理

在 Django 项目中,如果存在多个应用(Apps),使用 django_apscheduler 需要合理组织代码以避免任务冲突或重复执行。以下是详细的解决方案:

1. 核心原则
  • 避免多次启动调度器 :确保全局只有一个调度器实例(BackgroundScheduler),否则会引发线程冲突。

  • 集中管理任务注册 :推荐将所有任务的注册逻辑集中在一个位置(如某个 App 或中间件中),而非分散到多个 App 的 ready() 方法中。

  • 任务 ID 唯一性 :不同 App 中的任务需要唯一标识符(id 参数),避免覆盖或冲突。

2. 推荐方案:集中式调度器管理
步骤 1:创建一个专用 App(如 scheduler

bash

复制代码
python manage.py startapp scheduler
步骤 2:在 scheduler 中定义全局调度器

python

python 复制代码
# scheduler/jobs.py
from apscheduler.schedulers.background import BackgroundScheduler
from django_apscheduler.jobstores import DjangoJobStore
from django.conf import settings

# 全局调度器实例
scheduler = None

def init_scheduler():
    global scheduler
    if not scheduler:
        scheduler = BackgroundScheduler(timezone=settings.TIME_ZONE)
        scheduler.add_jobstore(DjangoJobStore(), "default")
    return scheduler
步骤 3:其他 App 定义任务(以 app1app2 为例)

python

python 复制代码
# app1/tasks.py
def app1_task():
    print("App1 定时任务执行")

# app2/tasks.py
def app2_task():
    print("App2 定时任务执行")
步骤 4:在 scheduler 中统一注册所有任务

python

python 复制代码
# scheduler/jobs.py(续)
from app1.tasks import app1_task
from app2.tasks import app2_task

def register_jobs():
    scheduler = init_scheduler()
    
    # 注册 app1 的任务(每天凌晨执行)
    scheduler.add_job(
        app1_task,
        trigger="cron", hour=0, minute=0,
        id="app1_daily_task",
        replace_existing=True  # 避免重复注册
    )
    
    # 注册 app2 的任务(每小时执行)
    scheduler.add_job(
        app2_task,
        trigger="interval", hours=1,
        id="app2_hourly_task",
        replace_existing=True
    )
    
    return scheduler
步骤 5:在 scheduler 的 AppConfig 中启动调度器

python

python 复制代码
# scheduler/apps.py
from django.apps import AppConfig
from django.conf import settings

class SchedulerConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'scheduler'

    def ready(self):
        if not settings.DEBUG:  # 生产环境启动
            from .jobs import init_scheduler, register_jobs
            scheduler = init_scheduler()
            register_jobs()
            try:
                scheduler.start()
            except KeyboardInterrupt:
                scheduler.shutdown()
步骤 6:配置 settings.py

python

python 复制代码
INSTALLED_APPS = [
    # ...
    'django_apscheduler',
    'app1',
    'app2',
    'scheduler',  # 确保最后加载,以便其他 App 的任务已定义
]
3. 替代方案:分散式任务注册(需谨慎)

如果希望每个 App 管理自己的任务,需确保调度器仅初始化一次:

在公共模块中定义调度器

python

python 复制代码
# core/scheduler.py
from apscheduler.schedulers.background import BackgroundScheduler
from django_apscheduler.jobstores import DjangoJobStore
from django.conf import settings

scheduler = BackgroundScheduler(timezone=settings.TIME_ZONE)
scheduler.add_jobstore(DjangoJobStore(), "default")

def get_scheduler():
    return scheduler

在每个 App 的 ready() 中注册任务

python

python 复制代码
# app1/apps.py
from django.apps import AppConfig
from core.scheduler import get_scheduler

class App1Config(AppConfig):
    def ready(self):
        scheduler = get_scheduler()
        if not scheduler.running:
            scheduler.start()
        
        # 注册任务
        scheduler.add_job(
            self.my_task,
            trigger="interval", minutes=30,
            id="app1_task",
            replace_existing=True
        )
    
    def my_task(self):
        print("App1 任务执行")

风险提示

  • 需确保 scheduler.start() 只调用一次。

  • 多个 App 的 ready() 加载顺序可能导致竞争条件。

4. 多实例部署的冲突处理

若项目部署多个实例(如多台服务器或多容器),需通过数据库锁避免重复执行:

python

python 复制代码
# scheduler/jobs.py(续)
from django_apscheduler.models import DjangoJobExecution

def register_jobs():
    scheduler = init_scheduler()
    
    # 注册任务时检查锁
    with transaction.atomic():
        job, created = DjangoJobExecution.objects.select_for_update().get_or_create(
            job_id="app1_daily_task",
            defaults={"status": "idle"}
        )
        if created:
            scheduler.add_job(...)
5. 项目结构示例
复制代码
myproject/
├── core/
│   └── scheduler.py          # 全局调度器定义(可选方案)
├── app1/
│   ├── tasks.py              # 定义 app1 的任务
│   ├── apps.py
├── app2/
│   ├── tasks.py              # 定义 app2 的任务
│   ├── apps.py
├── scheduler/                # 专用调度器 App
│   ├── jobs.py               # 注册所有任务
│   ├── apps.py
├── myproject/
│   ├── settings.py
│   └── urls.py

6. 注意事项

  1. 避免 DEBUG 模式重复启动 :在 settings.DEBUG=True 时,Django 可能会因代码重载多次初始化调度器,建议通过环境变量或 AppConfig 条件判断控制。

  2. 任务 ID 唯一性 :跨 App 的任务必须使用唯一 id,否则后注册的任务会覆盖先前的。

  3. 数据库清理 :定期清理 DjangoJobExecution 表,避免历史记录堆积:

    bash

    python 复制代码
    python manage.py delete_old_job_executions 7  # 删除7天前的记录
总结
  • 多 App 场景推荐集中式管理 :通过一个专用 App(如 scheduler)统一注册任务,确保调度器单例。

  • 动态任务需谨慎:若需动态添加任务(如通过 API),需保证线程安全和任务 ID 唯一性。

  • 生产环境高可用:多实例部署时结合数据库锁或外部协调服务(如 Redis 分布式锁)。

相关推荐
郝YH是人间理想33 分钟前
Python面向对象
开发语言·python·面向对象
藍海琴泉34 分钟前
蓝桥杯算法精讲:二分查找实战与变种解析
python·算法
Full Stack Developme1 小时前
SQL 版本历史
数据库·sql
声声codeGrandMaster3 小时前
Django项目入门
后端·mysql·django
杰克逊的日记4 小时前
mysql数据实时全量+增量迁移
数据库·mysql·数据迁移
linuxxx1105 小时前
centos7 升级MariaDB 到 10.5 或更高版本
数据库·mariadb
mqwguardain5 小时前
python常见反爬思路详解
开发语言·python
换个网名有点难5 小时前
django怎么配置404和500
数据库·django
_庄@雅@丽5 小时前
(UI自动化测试web端)第二篇:元素定位的方法_xpath扩展(工作当中用的比较多)
python·ui自动化元素定位·xpath元素定位
Adellle6 小时前
MySQL
数据库·后端·mysql