【后端】【Django】信号使用详解
Django post_save
信号使用详解(循序渐进)
一、信号的基本概念
Django 的 信号(Signal) 允许不同部分的代码在发生某些事件时进行通信,而不需要直接调用。这种机制可以解耦代码,让不同的模块独立工作。
post_save
信号:
- 触发时机:当 模型实例被保存 之后(无论是
create
还是update
)。 - 适用场景:创建关联对象、发送通知、记录日志等。
二、示例讲解
(一)场景介绍
假设我们有一个 User
模型,每个用户都应该有一个 Profile
(用户资料)。
我们希望在 用户创建后,自动为其创建一个 Profile,而不需要手动创建。
(二)创建 User
和 Profile
模型
在 models.py
中定义两个模型:
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE) # 一对一关系
bio = models.TextField(blank=True) # 个人简介
avatar = models.ImageField(upload_to='avatars/', null=True, blank=True) # 头像
Profile
通过OneToOneField
关联User
,表示每个User
只能有一个Profile
。bio
是用户的简介,默认为空。avatar
是用户头像,上传到avatars/
目录。
三、使用 post_save
信号自动创建 Profile
(一)编写 signals.py
在 signals.py
文件中添加信号处理逻辑:
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from .models import Profile
@receiver(post_save, sender=User) # 监听 User 的 post_save 信号
def create_user_profile(sender, instance, created, **kwargs):
"""当用户创建时,自动创建 Profile"""
if created: # 仅在用户新创建时执行
Profile.objects.create(user=instance)
解析:
post_save
监听User
模型的save()
事件。created
参数表示 是否是新创建的对象,只有created=True
时才执行Profile.objects.create(user=instance)
。@receiver(post_save, sender=User)
让 Django 知道这个函数是监听User
的post_save
信号的。
(二)注册信号
在 apps.py
里注册 signals.py
,确保 Django 加载它:
from django.apps import AppConfig
class MyAppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'myapp'
def ready(self):
import myapp.signals # 确保信号被加载
注意:
myapp
是应用名,import myapp.signals
让 Django 在应用启动时加载信号。
四、测试信号
(一)创建用户
进入 Django Shell:
python manage.py shell
执行以下代码:
from django.contrib.auth.models import User
from myapp.models import Profile
# 创建用户
user = User.objects.create(username='testuser', email='test@example.com')
# 检查 Profile 是否自动创建
print(Profile.objects.filter(user=user).exists()) # 预期输出: True
如果 Profile
成功创建,说明 post_save
信号正常工作。
(二)测试更新用户
user.first_name = "John"
user.save()
# 观察 Profile 是否变化(应该没有额外的 Profile 被创建)
print(Profile.objects.filter(user=user).count()) # 预期输出: 1
因为 post_save
只在 created=True
时创建 Profile
,所以更新用户时不会重复创建 Profile
。
五、改进优化
(一)防止重复创建
如果 Profile
可能会被手动删除,我们可以使用 get_or_create
避免重复创建:
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
"""当用户创建时,自动创建 Profile,如果已存在则不创建"""
if created:
Profile.objects.get_or_create(user=instance)
(二)用户删除时自动删除 Profile
如果 User
被删除,我们也希望 Profile
一起删除,可以使用 pre_delete
信号:
from django.db.models.signals import pre_delete
@receiver(pre_delete, sender=User)
def delete_user_profile(sender, instance, **kwargs):
"""当用户被删除时,自动删除 Profile"""
instance.profile.delete()
六、总结
post_save
监听User
模型的save()
,在用户 创建 后自动创建Profile
。@receiver(post_save, sender=User)
让 Django 识别信号。- 在
apps.py
里 导入 signals.py 以注册信号。 get_or_create
避免Profile
被误删后无法重新创建。- 使用
pre_delete
监听User
删除,并同步删除Profile
,避免遗留数据。
这样,我们实现了 用户注册自动创建 Profile,并确保数据一致性! 🚀
改进 post_save
信号,加入 软删除 机制
一、为什么要使用软删除?
- 直接删除
User
会导致Profile
及其他关联数据被彻底删除,可能不符合业务需求。 - 软删除 允许将
User
设为 “已删除”,但数据仍然存在,便于恢复和审计。 - 可以通过
is_deleted
字段标记用户是否已删除,而不是物理删除。
二、修改 User
模型,添加 is_deleted
软删除字段
Django 默认的 User
模型不能直接修改,我们可以 创建一个 User 扩展模型 或 创建一个 AbstractUser 自定义模型。
方法 1:扩展 User
在 models.py
中:
from django.contrib.auth.models import User
from django.db import models
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
is_deleted = models.BooleanField(default=False) # 软删除标记
方法 2:自定义 User
如果需要对 User
进行深度定制,可以继承 AbstractUser
:
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser):
is_deleted = models.BooleanField(default=False) # 软删除标记
然后在 settings.py
里修改:
AUTH_USER_MODEL = 'myapp.CustomUser'
三、修改 post_save
逻辑,防止软删除时重复创建 Profile
在 signals.py
中:
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth import get_user_model
from .models import Profile
User = get_user_model()
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
"""当用户创建时,自动创建 Profile,并考虑软删除"""
if created:
Profile.objects.get_or_create(user=instance)
elif instance.is_deleted:
# 如果用户被标记为删除,自动软删除 Profile
instance.profile.is_deleted = True
instance.profile.save()
四、修改 pre_delete
逻辑,防止物理删除
如果使用 pre_delete
监听 User
的删除,我们需要改成 软删除逻辑:
from django.db.models.signals import pre_delete
@receiver(pre_delete, sender=User)
def soft_delete_user(sender, instance, **kwargs):
"""用户删除时,不真正删除,而是标记 is_deleted"""
instance.is_deleted = True
instance.save()
这样,用户删除时不会被真正删除,而只是被标记。
五、查询用户时过滤掉已删除的用户
由于软删除后,is_deleted=True
的数据仍然存在,我们可以 修改默认查询集 过滤掉已删除的用户:
from django.contrib.auth.models import User
from django.db import models
class ActiveUserManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(is_deleted=False)
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
is_deleted = models.BooleanField(default=False)
objects = ActiveUserManager() # 默认只返回未删除的用户
all_objects = models.Manager() # 查询所有用户(包括软删除)
这样:
UserProfile.objects.all()
只返回 未删除的用户。UserProfile.all_objects.all()
返回 所有用户(包括已删除)。
六、测试软删除
在 Django Shell 里:
from django.contrib.auth.models import User
from myapp.models import Profile
# 创建用户
user = User.objects.create(username='testuser')
# 确保 Profile 被创建
print(Profile.objects.filter(user=user).exists()) # 预期输出: True
# 软删除用户
user.is_deleted = True
user.save()
# 观察 Profile 是否也被软删除
print(Profile.objects.get(user=user).is_deleted) # 预期输出: True
七、总结
is_deleted
软删除字段 代替物理删除,数据可恢复。- 修改
post_save
逻辑,确保用户删除时 Profile 也被标记为删除。 - 使用
pre_delete
软删除机制,避免User
被真正删除。 - 自定义
Manager
过滤软删除数据,防止查询时误取已删除用户。
这样,我们实现了 用户和 Profile 的软删除机制,确保数据安全且可恢复! 🚀