如何防止灯具与 django post_save 信号代码冲突?
Posted
技术标签:
【中文标题】如何防止灯具与 django post_save 信号代码冲突?【英文标题】:How do I prevent fixtures from conflicting with django post_save signal code? 【发布时间】:2011-03-30 19:29:02 【问题描述】:在我的应用程序中,我想在新用户注册时在某些表中创建条目。例如,我想创建一个用户资料,然后为他们引用他们的公司和其他一些记录。我用 post_save 信号实现了这个:
def callback_create_profile(sender, **kwargs):
# check if we are creating a new User
if kwargs.get('created', True):
user = kwargs.get('instance')
company = Company.objects.create(name="My Company")
employee = Employee.objects.create(company=company, name_first=user.first_name, name_last=user.last_name)
profile = UserProfile.objects.create(user=user, employee=employee, partner=partner)
# Register the callback
post_save.connect(callback_create_profile, sender=User, dispatch_uid="core.models")
这在运行时效果很好。我可以使用管理员创建一个新用户,而其他三个表也可以使用 sensible 获取条目。 (除了user.first_name和user.last_name之后的员工在保存时没有填写在admin的表单中。我还是不明白为什么会这样)
问题是在我运行我的测试套件时出现的。在此之前,我已经创建了一堆固定装置来在表格中创建这些条目。现在我收到一条错误消息:
IntegrityError: duplicate key value violates unique constraint "core_userprofile_user_id_key"
我认为这是因为我已经在 id 为“1”的夹具中创建了公司、员工和个人资料记录,现在 post_save 信号正在尝试重新创建它。
我的问题是:我可以在运行灯具时禁用这个 post_save 信号吗?我可以检测到我作为测试套件的一部分运行而不创建这些记录吗?我现在应该从夹具中删除这些记录吗(尽管信号只设置默认值而不是我想要测试的值)?为什么夹具加载代码不直接覆盖创建的记录?
人们如何做到这一点?
【问题讨论】:
【参考方案1】:我想我找到了一种方法来做到这一点。与信号一起传入的 kwargs 中有一个“原始”参数,所以我可以用这个替换上面的测试:
if (kwargs.get('created', True) and not kwargs.get('raw', False)):
在 loaddata 运行时使用 Raw。这似乎可以解决问题。
这里提到:http://code.djangoproject.com/ticket/13299
如果记录在案就好了:http://docs.djangoproject.com/en/1.2/ref/signals/#django.db.models.signals.post_save
【讨论】:
2 年后,这是我发现处理此问题的最佳方法。我必须为自定义用户配置文件 (AUTH_PROFILE_MODULE) 模型执行此操作,它会从我的应用程序中创建一小部分默认用户。我只是在我的自定义信号中检查了 kwargs 中是否“原始”。 很好的解决方案!今天早上你帮我省了很多麻烦。为了简化这一点,可以使用命名参数created
并删除多余的括号:if created and not kwargs.get('raw', False):
【参考方案2】:
这是一个老问题,但我发现最直接的解决方案是使用加载数据传递的“原始”参数,并装饰侦听器函数,例如:
from functools import wraps
def disable_for_loaddata(signal_handler):
@wraps(signal_handler)
def wrapper(*args, **kwargs):
if kwargs['raw']:
print "Skipping signal for %s %s" % (args, kwargs)
return
signal_handler(*args, **kwargs)
return wrapper
然后
@disable_for_loaddata
def callback_create_profile(sender, **kwargs):
# check if we are creating a new User
...
【讨论】:
值得指出的是,m2m 信号不提供“原始”标志。我不知道如何解决这个问题。【参考方案3】:简单的解决方案,将其添加到您的 post_save 函数的开头:
if kwargs.get('raw', False):
return False
这将导致该函数在加载夹具时退出。
见:https://docs.djangoproject.com/en/dev/ref/signals/#post-save
【讨论】:
【参考方案4】:我在我的一个项目中遇到了类似的问题。就我而言,信号也在减慢测试速度。我最终放弃了信号,转而重写 Model.save()
方法。
但是,在您的情况下,我认为通过覆盖任何 save()
方法来实现这一点没有意义。在这种情况下,您可能想尝试一下。警告,我只试过一次。它似乎可以工作,但没有经过彻底的测试。
-
创建your own test runner。
在加载固定装置之前,disconnect
callback_create_profile
函数来自 User
类的 post_save
信号。
让固定装置加载。
将函数连接回信号。
【讨论】:
哦,这也是一种方法......我不知道你可以断开信号。以上是关于如何防止灯具与 django post_save 信号代码冲突?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Django 信号(Pre_save,Post_save)从“B”模型的 ImageField 设置“A”模型的 ImageField
Django 从 post_save 信号访问 ManyToMany 字段
Unhashable 类型:尝试在 Django 中使用 post_save 动态添加 m2m 关系时出现“列表”错误