如何使用Django FSM将1天未活跃的用户设为非活跃状态
使用Django FSM自动将1天未活跃用户标记为非活跃状态
原代码存在的问题
- 条件函数
state_inactive违反Django FSM设计规则:条件函数仅需返回布尔值判断转换是否允许,不应直接修改字段或调用save() date字段为创建时间,无法跟踪用户最后活跃时间,不符合"1天未活跃"的需求逻辑- 缺少批量触发状态转换的执行逻辑,无法自动处理所有符合条件的用户
修正后的完整实现
1. 修正模型定义
替换原模型,新增last_active字段跟踪用户最后活跃时间,规范FSM状态转换逻辑:
from django.db import models from django_fsm import FSMField, transition import datetime class UserValidity(models.Model): STATE_CHOICES = ( ('active', '活跃'), ('inactive', '非活跃'), ) # 开启protected避免直接修改state字段,强制通过FSM转换 state = FSMField(default="active", choices=STATE_CHOICES, protected=True) name = models.CharField(max_length=10) # 记录用户最后活跃时间,创建时自动赋值,用户活跃时更新 last_active = models.DateTimeField(auto_now_add=True) # 条件函数:判断是否超过1天未活跃 def is_eligible_for_inactive(self): one_day_ago = datetime.datetime.now() - datetime.timedelta(days=1) return self.last_active < one_day_ago @transition(field=state, source="active", target="inactive", conditions=[is_eligible_for_inactive]) def mark_as_inactive(self): # 可在此添加状态转换后的额外逻辑(如发送通知、记录日志) print(f"用户 {self.name} 已切换为非活跃状态")
2. 批量处理未活跃用户
方式一:Django自定义命令(适合测试或手动触发)
在app目录下创建management/commands/mark_inactive_users.py:
from django.core.management.base import BaseCommand from your_app.models import UserValidity class Command(BaseCommand): help = '批量将1天未活跃的活跃用户标记为非活跃' def handle(self, *args, **options): processed_count = 0 # 筛选所有活跃用户,逐一尝试状态转换 for user in UserValidity.objects.filter(state='active'): # mark_as_inactive()仅在满足条件时执行转换并返回True if user.mark_as_inactive(): processed_count += 1 self.stdout.write(self.style.SUCCESS(f"完成处理,共标记 {processed_count} 个用户"))
执行命令:
python manage.py mark_inactive_users
方式二:Celery定时任务(生产环境自动执行)
创建Celery任务实现每日自动处理:
from celery import shared_task from your_app.models import UserValidity @shared_task def auto_mark_inactive_users(): processed_count = 0 for user in UserValidity.objects.filter(state='active'): if user.mark_as_inactive(): processed_count += 1 return f"每日非活跃用户标记完成,共处理 {processed_count} 个用户"
在Celery配置中添加定时任务(例如每日凌晨执行):
from celery.schedules import crontab CELERY_BEAT_SCHEDULE = { 'daily-mark-inactive-users': { 'task': 'your_app.tasks.auto_mark_inactive_users', 'schedule': crontab(hour=0, minute=0), }, }
3. 更新用户活跃时间
当用户产生活跃行为(如登录、操作)时,更新last_active字段:
# 示例:用户登录时更新活跃时间 user = UserValidity.objects.get(name="test_user") user.last_active = datetime.datetime.now() user.save()
关键注意事项
- 始终通过FSM的转换方法修改状态,避免直接赋值
state字段,保证状态转换的合法性 - 使用
DateTimeField而非DateField,可以更精确地处理跨天的未活跃判断 - 批量处理时不要直接用
update()修改state,必须逐个调用转换方法,确保触发FSM的钩子逻辑
内容的提问来源于stack exchange,提问作者TMD
相关产品推荐
相关产品推荐

