如何防止fixtures与django post_save信号代码冲突?
在我的应用程序中,我想在新用户注册时,在某些表中创建记录。比如说,我想创建一个用户资料,这个资料会关联他们的公司和其他一些记录。我是通过一个叫做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")
这个方法运行得很好。我可以通过管理后台创建一个新用户,然后其他三个表也会相应地生成记录,数据也很合理。(不过有一点例外,就是员工的记录,因为在管理后台的表单中,用户的名字和姓氏在保存时没有填写。我还是不太明白为什么会这样)
问题出现在我运行测试套件的时候。在这之前,我已经创建了一些固定数据,用来在表中生成这些记录。现在我遇到了一个错误,提示如下:
IntegrityError: duplicate key value violates unique constraint "core_userprofile_user_id_key"
我觉得这可能是因为我已经在固定数据中创建了一个公司、员工和资料记录,ID是“1”,而现在post_save信号又试图重新创建这些记录。
我想问的是:在运行固定数据时,我能否禁用这个post_save信号?我能否检测到我是在测试套件中运行,从而不创建这些记录?我现在应该把这些记录从固定数据中删除吗(虽然这个信号只是设置默认值,而不是我想要测试的具体值)?为什么固定数据加载的代码不直接覆盖已创建的记录呢?
大家通常是怎么处理这个问题的呢?
4 个回答
简单的解决办法,在你的 post_save 函数开头加上这个:
if kwargs.get('raw', False):
return False
这样做会让这个函数在加载数据时直接退出。
详情请查看:https://docs.djangoproject.com/en/dev/ref/signals/#post-save
这是一个老问题,不过我找到的最简单的解决办法是使用'raw'这个参数,通过加载数据来实现,并且给监听函数加上一些装饰,比如:
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
...
我想我找到了一个解决办法。在传递信号时,有一个叫做'raw'的参数可以用在kwargs里,所以我可以把我之前的测试换成这个:
if (kwargs.get('created', True) and not kwargs.get('raw', False)):
这个'raw'参数是在loaddata运行的时候用的。看起来这样就能解决问题。
这里提到过这个内容:http://code.djangoproject.com/ticket/13299
如果能在文档中说明这一点就好了:http://docs.djangoproject.com/en/stable/ref/signals/#django.db.models.signals.post_save