如何防止fixtures与django post_save信号代码冲突?

58 投票
4 回答
11783 浏览
提问于 2025-04-16 02:53

在我的应用程序中,我想在新用户注册时,在某些表中创建记录。比如说,我想创建一个用户资料,这个资料会关联他们的公司和其他一些记录。我是通过一个叫做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 个回答

16

简单的解决办法,在你的 post_save 函数开头加上这个:

if kwargs.get('raw', False):
    return False

这样做会让这个函数在加载数据时直接退出。

详情请查看:https://docs.djangoproject.com/en/dev/ref/signals/#post-save

26

这是一个老问题,不过我找到的最简单的解决办法是使用'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
    ...
87

我想我找到了一个解决办法。在传递信号时,有一个叫做'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

撰写回答