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
相关产品推荐
相关产品推荐

