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

发布于 2024-09-14 11:35:04 字数 1191 浏览 6 评论 0原文

在我的应用程序中,我想在新用户注册时在某些表中创建条目。例如,我想创建一个用户配置文件,然后该配置文件将引用他们的公司和他们的一些其他记录。我用 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")

这在运行时效果很好。我可以使用管理员创建一个新用户,其他三个表也可以获取合理的条目。 (除此之外,由于 user.first_name 和 user.last_name 在保存时没有在管理员表单中填写,因此我仍然不明白为什么这样做)

问题出现在我运行测试时套房。在此之前,我创建了一堆固定装置来在表中创建这些条目。现在我收到一个错误,指出:

IntegrityError: duplicate key value violates unique constraint "core_userprofile_user_id_key"

我认为这是因为我已经在 ID 为“1”的装置中创建了一家公司、员工和个人资料记录,现在 post_save 信号正在尝试重新创建它。

我的问题是:运行灯具时可以禁用此 post_save 信号吗?我可以检测到我正在作为测试套件的一部分运行而不创建这些记录吗?我现在应该从灯具中删除这些记录吗(尽管信号仅设置默认值而不是我想要测试的值)?为什么夹具加载代码不覆盖创建的记录?

人们如何做到这一点?

In my application, I want to create entries in certain tables when a new user signs up. For instance, I want to create a userprofile which will then reference their company and some other records for them. I implemented this with a post_save signal:

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")

This works well when run. I can use the admin to create a new user and the other three tables get entries with sensible as well. (Except that is, the employee since the user.first_name and user.last_name aren't filled out in the admin's form when it saves. I still don't understand why it is done like that)

The problem came when I ran my test suite. Before this, I had created a bunch of fixtures to create these entries in the tables. Now I get an error that states:

IntegrityError: duplicate key value violates unique constraint "core_userprofile_user_id_key"

I think this is because I have already created a company,employee and profile records in the fixture with id "1" and now the post_save signal is trying to recreate it.

My questios are: can I disable this post_save signal when running fixtures? Can I detect that I am running as part of the test suite and not create these records? Should I delete these records from the fixtures now (although the signal only sets defaults not the values I want to be testing against)? Why doesn't the fixture loading code just overwrite the created records?

How do people do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

幽梦紫曦~ 2024-09-21 11:35:04

我想我找到了一种方法来做到这一点。与信号一起传入的 kwargs 中有一个“raw”参数,因此我可以用以下参数替换上面的测试:

if (kwargs.get('created', True) and not kwargs.get('raw', False)):

在 loaddata 运行时使用 Raw。这似乎可以解决问题。

这里提到: http://code.djangoproject.com/ticket/13299

将是如果有记录就好了: http:// /docs.djangoproject.com/en/stable/ref/signals/#django.db.models.signals.post_save

I think I figured out a way to do this. There is a 'raw' parameter in the kwargs passed in along with signals so I can replace my test above with this one:

if (kwargs.get('created', True) and not kwargs.get('raw', False)):

Raw is used when loaddata is running. This seems to do the trick.

It is mentioned here: http://code.djangoproject.com/ticket/13299

Would be nice if this was documented: http://docs.djangoproject.com/en/stable/ref/signals/#django.db.models.signals.post_save

土豪 2024-09-21 11:35:04

这是一个老问题,但我发现最简单的解决方案是使用由加载数据传递的“原始”参数,并装饰侦听器函数,例如:

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
    ...

This is an old question, but the solution I've found most straightforward is to use the 'raw' argument, passed by load data, and decorate the listener functions, for example:

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

and then

@disable_for_loaddata
def callback_create_profile(sender, **kwargs):
    # check if we are creating a new User
    ...
比忠 2024-09-21 11:35:04

简单的解决方案,将其添加到 post_save 函数的开头:

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

这将导致该函数在加载夹具时退出。

请参阅: https://docs.djangoproject.com/en/dev/参考/信号/#post-save

Simple solution, add this to the beginning of your post_save function:

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

This will cause this function to exit when loading a fixture.

See: https://docs.djangoproject.com/en/dev/ref/signals/#post-save

羁拥 2024-09-21 11:35:04

我在我的一个项目中遇到了类似的问题。就我而言,信号也减慢了测试速度。我最终放弃了信号,转而重写 Model.save() 方法。

但就您而言,我认为通过重写任何 save() 方法来实现此目的没有意义。在这种情况下,您可能想尝试一下。警告,我只尝试过一次。它似乎有效,但尚未经过彻底测试。

  1. 创建您自己的测试运行程序
  2. 在加载灯具之前,断开连接 来自 User 类的 post_save 信号的 callback_create_profile 函数。
  3. 让夹具加载。
  4. 将函数连接回信号。

I faced a similar problem in one of my projects. In my case the signals were slowing down the tests as well. I ended up abandoning signals in favour of overriding a Model.save() method instead.

In your case however I don't think it makes sense to achieve this by overriding any save() methods. In that case you might want to try this. Warning, I only tried it once. It seemed to work but is not thoroughly tested.

  1. Create your own test runner.
  2. Before you load the fixtures, disconnect the callback_create_profile function from the User class' post_save signal.
  3. Let the fixtures load.
  4. Connect the function back to the signal.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文