Django:bookings创建Contact时core自动生成User的信号配置问题
问题解决:创建Contact时通过信号关联User触发非空约束错误
问题场景
在bookings应用创建Contact实例时,需同时在独立的core应用中创建/关联User实例,逻辑为:检查User是否存在,存在则关联到Contact,不存在则新建User后关联。即使设置了user字段的null=False, blank=True,仍触发数据库非空约束错误。
错误信息
IntegrityError at /bookings/contacts/
null value in column "user_id" of relation "bookings_contact" violates not-null constraint
DETAIL: Failing row contains (18, tesing@testing.com, 5555555, null).
相关代码
bookings/models.py
from django.db import models from django.conf import settings from django.contrib import admin import datetime class Contact(models.Model): email = models.EmailField(max_length=255, null=True, blank=True) phone = models.CharField(max_length=255, null=False, blank=True) user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=False, blank=True)
core/models.py
from django.contrib.auth.models import AbstractUser from django.db import models """ 在Core应用中创建User以解耦core与bookings应用。邮箱唯一约束在serializers.py和admin.py中处理 """ class User(AbstractUser): email = models.EmailField(unique=True)
core/signals/handlers.py
from django.db.models.signals import post_save from django.dispatch import receiver from django.utils.crypto import get_random_string from bookings.models import Contact from core.models import User @receiver(post_save, sender=Contact) def create_user_for_new_contact(sender, instance, created, **kwargs): if created and instance.email: email = instance.email try: user = User.objects.get(email=email) except User.DoesNotExist: password = get_random_string(length=12) user = User.objects.create(email=email, password=password) instance.user = user instance.save()
core/apps.py
from django.apps import AppConfig class CoreConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'core' def ready(self) -> None: import core.signals.handlers
问题原因
使用post_save信号时,Contact实例已经被写入数据库,此时user字段为null,但模型设置了null=False,直接触发数据库非空约束。信号逻辑是在Contact保存后才去创建/关联User,无法覆盖初始保存时的user字段值。
解决方案
将信号从post_save改为pre_save,在Contact实例写入数据库前就完成User的创建/关联,确保保存Contact时user字段已有有效值。
修改后的core/signals/handlers.py代码:
from django.db.models.signals import pre_save from django.dispatch import receiver from django.utils.crypto import get_random_string from bookings.models import Contact from core.models import User @receiver(pre_save, sender=Contact) def create_user_for_new_contact(sender, instance, **kwargs): # 通过instance.pk是否为None判断是否是新创建的Contact if instance.pk is None and instance.email: email = instance.email try: user = User.objects.get(email=email) except User.DoesNotExist: password = get_random_string(length=12) user = User.objects.create(email=email, password=password) # 直接赋值user字段,pre_save后Django会自动保存Contact,无需手动调用instance.save() instance.user = user
额外注意事项
- 确保
settings.py中已配置自定义User模型:AUTH_USER_MODEL = 'core.User',避免关联默认User模型导致错误。 - 如果业务允许Contact无email的情况,需调整模型的
user字段为null=True,或者补充无email时的处理逻辑,避免再次触发非空约束。
内容的提问来源于stack exchange,提问作者Angus McDonald
相关产品推荐
相关产品推荐

