【问题标题】:In Django, when I call User.objects.create_user(username, email, password) - why does post_save get called twice?在 Django 中,当我调用 User.objects.create_user(username, email, password) - 为什么 post_save 会被调用两次?
【发布时间】:2012-02-13 14:07:31
【问题描述】:

在 views.py 中,我有以下视图,当注册新用户帐户时会调用该视图。它所做的只是从请求中获取用户名、电子邮件和密码,然后尝试使用这些凭据创建用户。在下面的代码中,“A”被打印,但“B”没有,因为它崩溃了:

views.py

def register(request):
    if request.method == 'POST':
        query_dict = request.POST
        username = query_dict['username']
        email = query_dict['user_email']
        password = query_dict['password']
        role = query_dict['role']
        print "A"
        user = User.objects.create_user(username, email, password)
        # the handler is called here and creates the user profile
        print "B"
        user = authenticate(username=username, password=password)
        user_profile = user.get_profile()
        user_profile.role = role
        user_profile.save()
        if user is not None and user.is_active:
            login(request, user)
            return HttpResponseRedirect("/")

myapp/models.py 我有以下处理程序代码。

'models.py`

post_save.connect(create_user_profile, sender=User)

def create_user_profile(sender, instance, created, **kwargs):
    print "created="+str(created)
    if created: # as opposed to being saved after being created earlier, I assume
        print "attempting to create a user profile for "+instance.username
        up = UserProfile.objects.create(user=instance)
        print "created a new UserProfile: <"+str(up)+">"

当我运行我的应用程序时,打印输出是:

A
created=True
attempting to create a user profile for john
created a new UserProfile: <john - >
created=True
attempting to create a user profile for john
~~~~CRASHES~~~~

在过去几天的整个开发过程中,我注意到create_user_profile 方法在我运行User.objects.create_user(username, email, password) 时被调用了两次我直接从文档中复制了处理程序代码,所以我很确定它是正确的( https://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users)。我不确切知道在 django 代码中的哪个位置调用 create_user_profile 方法,但我假设它仅在调用处理程序的保存方法是由于创建对象时才将 created 标志设置为 true .在过去的几天里,这段代码一直在正常工作,但突然之间,当处理程序第二次调用 create_user_profile 方法时,created 标志设置为 true,就像第一次一样.这导致我的应用程序因IntegrityError 而崩溃,并抱怨它无法创建具有相同主键的两个对象。

所以有两件事我不明白。第一:当我只调用一次User.objects.create_user(username, email, password)时,为什么post_save会发生两次;第二:为什么created标志突然停止工作?

如何调试?

谢谢

【问题讨论】:

    标签: django django-models django-users django-errors


    【解决方案1】:

    您的 models.py 文件可能会被导入并运行两次。在这种情况下,同一个信号处理程序将被附加两次,并在保存模型实例时触发两次。

    如果您将其导入一次,则很容易发生这种情况

    import myapp.models
    

    又是

    import myproject.myapp.models
    

    在文件顶部的控制台上打印一些内容可能会让您了解是否正在发生这种情况。

    无论如何,您可以通过给它一个唯一的dispatch_uid 来防止信号处理程序被多次注册。 Django 将检查Signal.connect 以查看处理程序是否已为具有相同 UID 的同一发件人注册,如果已注册,则不会重新注册。

    将您的信号连接代码更改为类似

    post_save.connect(create_user_profile, sender=User, dispatch_uid='create_user_profile')
    

    看看有没有帮助

    【讨论】:

    • 哇,谢谢。事实证明,models.py 被导入了两次。如果没有 StackOverflow,我认为在这周剩下的时间里我无法调试它。你的 UID 技巧也奏效了。
    • 我无法弄清楚 models.py 是如何被导入两次的。我可以在文件顶部的打印语句中添加代码,让我知道是谁或如何导入它的吗?现在它似乎在我启动 runserver 时第一次被导入,一旦我开始对我的应用程序执行任何操作,它就会被第二次导入。
    • 糟糕,我好像说得太早了。仔细查看我的打印输出,我发现我的应用程序再次开始工作,这让我认为您的解决方案有效,但即使我按照您的建议设置了 dispatch_uidcreate_user_profile 方法仍然被调用了两次。这一次created 标志第二次为假,这就是为什么事情又恢复了。但是,我担心我有某种竞争条件,并且不知道标志何时会在第二次调用时恢复为真。所以,即使我设置了dispatch_uid,该方法也被调用了两次。
    • 我相信您的信号第二次被update_last_login 方法调用。这是由login(request, user) 调用触发的,它 调用User.save()。不过,这一次,用户不是新用户,所以createdFalse。一切都按应有的方式工作,这就是您在信号处理程序中测试创建的原因。
    • 好的,我现在明白了。它总是被调用两次,但它们并不总是相同的两次调用。您的 UID 方法确实有效,因为当我删除 UID 时,该方法在打印“B”之前被调用两次,因为该方法被注册了两次,但是当我将 UID 放入第二个方法调用时,发生在打印“B”之后。非常感谢。
    猜你喜欢
    • 2020-05-24
    • 2011-01-21
    • 1970-01-01
    • 2012-08-02
    • 2021-07-13
    • 2021-11-25
    • 2015-12-24
    • 2012-12-10
    相关资源
    最近更新 更多