You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中如何定时自动修改模型字段并发送邮件?有无非Celery方案?

订单两周后自动触发邮件与资金解锁方案

首先需要给你的Order模型补充必要字段,用来追踪订单创建时间和提醒邮件发送状态:

class Order(models.Model):
    funds_unlocked = models.BooleanField(default=False)
    order_created = models.DateTimeField(auto_now_add=True)  # 记录订单创建时间
    reminder_sent = models.DateTimeField(null=True, blank=True)  # 记录提醒邮件发送时间
    # 补充关联字段:比如 user(下单用户)、vendor(商家)、amount(订单金额)等

    def save(self, *args, **kwargs):
        # 获取修改前的资金解锁状态
        old_funds_unlocked = False
        if self.pk:
            old_obj = Order.objects.get(pk=self.pk)
            old_funds_unlocked = old_obj.funds_unlocked
        
        super().save(*args, **kwargs)
        
        # 状态从False变为True时,执行商家余额增加逻辑
        if not old_funds_unlocked and self.funds_unlocked:
            # 示例:增加商家余额
            # self.vendor.balance += self.amount
            # self.vendor.save()
            pass

方法一:定时脚本 + 系统Crontab(最简便,无额外依赖)

这种方法不需要引入异步任务框架,适合小型项目,通过定时查询数据库处理符合条件的订单。

步骤1:编写Django管理命令

1.1 发送提醒邮件的命令

在你的app目录下创建management/commands/auto_send_reminder.py:

from django.core.management.base import BaseCommand
from django.utils import timezone
from datetime import timedelta
from your_app.models import Order
from django.core.mail import send_mail

class Command(BaseCommand):
    help = '给下单超过两周未确认的用户发送收货提醒邮件'

    def handle(self, *args, **options):
        # 计算两周前的时间节点
        two_weeks_ago = timezone.now() - timedelta(weeks=2)
        # 筛选符合条件的订单:未解锁资金、未发送过提醒、创建超过两周
        target_orders = Order.objects.filter(
            order_created__lte=two_weeks_ago,
            funds_unlocked=False,
            reminder_sent__isnull=True
        )

        for order in target_orders:
            # 发送提醒邮件
            send_mail(
                subject='请确认您的订单收货状态',
                message=f'您好,您的订单{order.id}已下单超过两周,请确认是否收到包裹。若3天内未回复,我们将自动解锁商家资金。',
                from_email='your-service@example.com',
                recipient_list=[order.user.email],
                fail_silently=False,
            )
            # 更新提醒发送时间
            order.reminder_sent = timezone.now()
            order.save()
        
        self.stdout.write(self.style.SUCCESS(f'已发送{len(target_orders)}封提醒邮件'))

1.2 自动解锁资金的命令

创建management/commands/auto_unlock_funds.py:

from django.core.management.base import BaseCommand
from django.utils import timezone
from datetime import timedelta
from your_app.models import Order

class Command(BaseCommand):
    help = '给提醒邮件发送3天后未确认的订单自动解锁资金'

    def handle(self, *args, **options):
        # 计算3天前的时间节点
        three_days_ago = timezone.now() - timedelta(days=3)
        # 筛选符合条件的订单:未解锁资金、已发送提醒且超过3天
        target_orders = Order.objects.filter(
            reminder_sent__lte=three_days_ago,
            funds_unlocked=False
        )

        # 批量更新,提升效率
        updated_count = target_orders.update(funds_unlocked=True)
        self.stdout.write(self.style.SUCCESS(f'已自动解锁{updated_count}个订单的资金'))

步骤2:配置系统Crontab

编辑系统定时任务,让脚本每天自动执行:

# 打开crontab编辑界面
crontab -e
# 添加以下两行(替换为你的虚拟环境和项目路径)
0 0 * * * /path/to/venv/bin/python /path/to/project/manage.py auto_send_reminder
0 1 * * * /path/to/venv/bin/python /path/to/project/manage.py auto_unlock_funds
  • 第一行:每天0点执行发送提醒邮件的命令
  • 第二行:每天1点执行自动解锁资金的命令

如果需要更高频率的执行(比如每小时一次),可以调整Crontab的时间规则,比如0 * * * *。

方法二:使用Django Q(轻量异步任务框架,替代Celery)

Django Q是轻量级的异步任务工具,用数据库作为消息队列,不需要Redis/RabbitMQ,比Celery配置简单,适合中等规模项目。

步骤1:安装并配置Django Q

# 安装依赖
pip install django-q

在项目settings.py中添加配置:

INSTALLED_APPS = [
    # ... 其他应用
    'django_q',
]

Q_CLUSTER = {
    'name': 'DjangoORM',
    'workers': 4,  # 执行任务的工作进程数
    'timeout': 90,  # 任务超时时间
    'retry': 120,  # 任务重试间隔
    'queue_limit': 50,  # 队列最大任务数
    'bulk': 10,
    'orm': 'default'  # 使用默认数据库作为消息队列
}

执行数据库迁移:

python manage.py migrate

启动Django Q任务集群:

python manage.py qcluster

步骤2:编写任务函数

在你的app目录下创建tasks.py:

from django.core.mail import send_mail
from your_app.models import Order
from django.utils import timezone
from django_q.models import Schedule

def send_order_reminder(order_id):
    """发送订单收货提醒邮件"""
    try:
        order = Order.objects.get(id=order_id, funds_unlocked=False, reminder_sent__isnull=True)
        send_mail(
            subject='请确认您的订单收货状态',
            message=f'您好,您的订单{order.id}已下单超过两周,请确认是否收到包裹。若3天内未回复,我们将自动解锁商家资金。',
            from_email='your-service@example.com',
            recipient_list=[order.user.email],
            fail_silently=False,
        )
        order.reminder_sent = timezone.now()
        order.save()
    except Order.DoesNotExist:
        pass

def unlock_order_funds(order_id):
    """自动解锁订单资金"""
    try:
        order = Order.objects.get(id=order_id, funds_unlocked=False)
        order.funds_unlocked = True
        order.save()
    except Order.DoesNotExist:
        pass

def cancel_pending_tasks(order_id):
    """用户手动确认后,取消待执行的定时任务"""
    Schedule.objects.filter(name__in=[f'reminder_{order_id}', f'unlock_{order_id}']).delete()

步骤3:创建订单时添加定时任务

在创建订单的视图或信号中,添加延迟执行的任务:

from django.utils import timezone
from datetime import timedelta
from django_q.tasks import schedule
from your_app.tasks import send_order_reminder, unlock_order_funds
from your_app.models import Order

# 示例:创建订单的视图逻辑
def create_order(request):
    # ... 订单创建逻辑
    order = Order.objects.create(
        user=request.user,
        vendor=vendor,
        amount=amount,
        # 其他字段
    )

    # 安排两周后发送提醒邮件
    schedule(
        'your_app.tasks.send_order_reminder',
        order.id,
        scheduled_time=timezone.now() + timedelta(weeks=2),
        name=f'reminder_{order.id}'
    )

    # 安排两周+3天后自动解锁资金
    schedule(
        'your_app.tasks.unlock_order_funds',
        order.id,
        scheduled_time=timezone.now() + timedelta(weeks=2, days=3),
        name=f'unlock_{order.id}'
    )

    # ... 其他响应逻辑

步骤4:用户手动确认时取消任务

修改Order模型的save方法,当用户手动确认收货(funds_unlocked变为True)时,取消对应的定时任务:

def save(self, *args, **kwargs):
    old_funds_unlocked = False
    if self.pk:
        old_obj = Order.objects.get(pk=self.pk)
        old_funds_unlocked = old_obj.funds_unlocked
    
    super().save(*args, **kwargs)
    
    if not old_funds_unlocked and self.funds_unlocked:
        # 用户手动确认,取消待执行的定时任务
        from your_app.tasks import cancel_pending_tasks
        cancel_pending_tasks(self.id)
        # 执行商家余额增加逻辑
        # self.vendor.balance += self.amount
        # self.vendor.save()

两种方法对比

  • 定时脚本+Crontab:零额外依赖,配置简单,适合小型项目;缺点是实时性依赖Crontab的执行频率,无法精确到分钟级触发。
  • Django Q:轻量易配置,支持精确时间触发任务,适合中等规模项目;需要启动一个常驻的任务集群进程。

内容的提问来源于stack exchange,提问作者computerabuser

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 16:27:36