You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

额外注意事项

  1. 确保settings.py中已配置自定义User模型:AUTH_USER_MODEL = 'core.User',避免关联默认User模型导致错误。
  2. 如果业务允许Contact无email的情况,需调整模型的user字段为null=True,或者补充无email时的处理逻辑,避免再次触发非空约束。

内容的提问来源于stack exchange,提问作者Angus McDonald

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 17:24:57