当前位置: 首页 > article >正文

【后端】【Django】信号使用详解

Django post_save 信号使用详解(循序渐进)

一、信号的基本概念

Django 的 信号(Signal) 允许不同部分的代码在发生某些事件时进行通信,而不需要直接调用。这种机制可以解耦代码,让不同的模块独立工作。

post_save 信号

  • 触发时机:当 模型实例被保存 之后(无论是 create 还是 update)。
  • 适用场景:创建关联对象、发送通知、记录日志等。

二、示例讲解

(一)场景介绍

假设我们有一个 User 模型,每个用户都应该有一个 Profile(用户资料)。
我们希望在 用户创建后自动为其创建一个 Profile,而不需要手动创建。

(二)创建 UserProfile 模型

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 知道这个函数是监听 Userpost_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()

六、总结

  1. post_save 监听 User 模型的 save(),在用户 创建 后自动创建 Profile
  2. @receiver(post_save, sender=User) 让 Django 识别信号。
  3. apps.py导入 signals.py 以注册信号。
  4. get_or_create 避免 Profile 被误删后无法重新创建。
  5. 使用 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

七、总结

  1. is_deleted 软删除字段 代替物理删除,数据可恢复。
  2. 修改 post_save 逻辑,确保用户删除时 Profile 也被标记为删除。
  3. 使用 pre_delete 软删除机制,避免 User 被真正删除。
  4. 自定义 Manager 过滤软删除数据,防止查询时误取已删除用户。

这样,我们实现了 用户和 Profile 的软删除机制,确保数据安全且可恢复! 🚀


http://www.kler.cn/a/612232.html

相关文章:

  • C#面向对象 一些细节
  • 基于C++实现一个平面上的形状编辑程序
  • ChatGPT 4o 更新了图像能力,效果怎么样?
  • 青否数字人直播系统包括六大互动功能,保障直播间能够实现智能化实时互动!
  • RSA算法深度解析:从数学基础到安全实践
  • Docker容器的kafka在VM虚拟机挂起重新运行之后连接异常解决
  • 【人工智能】一部正在书写的传奇,从诞生到未来蓝图
  • 【力扣hot100题】(007)无重复字符的最长子串
  • Rust从入门到精通之进阶篇:17.宏编程基础
  • 排序算法(插入,希尔,选择,冒泡,堆,快排,归并)
  • 漫画|基于SprinBoot+vue的漫画网站(源码+数据库+文档)
  • Docker+Ollama+Xinference+RAGFlow+Dify+Open webui部署及踩坑问题
  • ctfhow——web入门171~175
  • 量子计算与项目管理:2025年颠覆性技术将如何重构任务分解逻辑?
  • 优雅的开始一个Python项目
  • C++ :顺序容器
  • Causality Based Front-door Defense AgainstBackdoor Attack on Language Models
  • 智能制造:自动化焊装线的数字化设计
  • Axios企业级封装实战:从拦截器到安全策略!!!
  • 基于HTML的邮件发送状态查询界面设计示例