Django 监听器没有听到信号

Django listener isn't hearing the signal

我试图在激活时(而不是在创建用户时)保存用户配置文件,但侦听器没有调用 create_user_profile()。侦听器位于 models.py.

from registration.signals import user_activated
from mysite.signals import create_user_profile

user_activated.connect(create_user_profile, sender=User)

我正在使用 django-registration-redux。我没有在注册过程中覆盖任何内容。在 registration/signals.py 中,信号是:

user_activated = Signal(providing_args=["user", "request"])

registration/default/views.py中有这个函数:

def activate(self, request, activation_key):
    """
    Given an an activation key, look up and activate the user
    account corresponding to that key (if possible).

    After successful activation, the signal
    ``registration.signals.user_activated`` will be sent, with the
    newly activated ``User`` as the keyword argument ``user`` and
    the class of this backend as the sender.

    """
    activated_user = RegistrationProfile.objects.activate_user(activation_key)
    if activated_user:
        signals.user_activated.send(sender=self.__class__,
                                    user=activated_user,
                                    request=request)

在 PyCharm 中,我在调用 send() 的最后一行放置了一个断点。当用户被激活时,执行会在该断点处暂停,并从那里继续执行,不会出现错误消息。就好像听者根本不存在一样。

可能发件人应该是 User class:

signals.user_activated.send(sender=activated_user.__class__,
                            user=activated_user,
                            request=request)

alecxe给出的答案是被接受的答案。

我添加这个答案只是为了帮助像我这样的新手理解这里发生的事情,并提供一种在不更改包代码的情况下更改侦听器的替代解决方案。

非常感谢@alecxe 的回答。这是最终帮助我全神贯注于整个过程的关键。在这种情况下,文档对我帮助不大,似乎其他许多人也对信号有困难。

关键是 send() 元组中的 sender 必须匹配 connect() 元组中的 sender

在这种情况下,发件人没有问题。问题是我的听众正在收听正确的信号,但却收听了错误的发送者。更改 send() 元组有效,但我宁愿修复损坏的侦听器也不愿修改注册包中的发件人。难点来了,我经验不足,不知道输出时sender=self.__class__是怎么出现的。使用带有断点的 PyCharm,我能够得到那个答案。

我会详细说明我是怎么做的,以防万一有人可以从中受益。

使用 alexce 的回答,我的信号侦听器正在工作,所以我能够在 create_user_profile() 中放置一个断点。这会在信号仍在内存中时暂停程序。此时,可以在调试器的变量列表中看到该信号。

我在 signals.py:

中的 Signal 参数中添加了 'check_signal'
user_activated = Signal(providing_args=["user", "request", "check_signal",])

...然后 self.__class__ 被添加回 send() 调用:

signals.user_activated.send(sender=activated_user.__class__,
                            user=activated_user,
                            request=request,
                            check_signal=self.__class__)

调试时,check_signal的值为<registration.backends.default.views.ActivationView>

所以在不修改发送者的情况下修复我的监听器的解决方案是首先恢复registration/signals.py:

中的原始代码
user_activated = Signal(providing_args=["user", "request"])

...并恢复registration/default/views.py:

中的代码
signals.user_activated.send(sender=self.__class__,
                            user=activated_user,
                            request=request)

...最后,修复models.py中的监听器:

from registration.backends.default.views import ActivationView
from registration.signals import user_activated
from mysite.signals import create_user_profile

user_activated.connect(create_user_profile, sender=ActivationView)

可能有一种无需导入 ActivationView 即可执行此操作的方法,但我不确定。它确实以这种方式工作。对于任何使用简单(无电子邮件)配置的 django-registration-redux 的人来说,唯一的区别是 from registration.backends.simple.views import RegistrationView 而监听器是 user_registered.connect(create_user_profile, sender=RegistrationView).

为了以防万一有人好奇,这里是来自 mysite/signals.py:

的代码
def create_user_profile(sender, user, **kwargs):
    """
    When user is activated, create the UserProfile. Prevents dead profiles
    from registered users who never activate.
    """
    from mysite.models import UserProfile

    UserProfile(user=user).save()

我花了几天疯狂和绝望的时间来获取这七行代码背后的逻辑。我希望这可以让其他人摆脱这种压力。