设计:如果我的模型有after_create回调,则打印确认令牌无效

时间:2014-03-07 00:01:11

标签: ruby-on-rails devise devise-confirmable

我在Rails 4.0.3应用程序上使用Devise 3.2.3

在我的用户模型中,我有一个after_create: subscribe回调版,用于订阅新用户的简报。每次新用户尝试确认他们的电子邮件时,我都会介绍此回调,然后会收到confirmation token is invalid条消息。然而,重发确认电子邮件的确认链接有效。

我显然可以避免使用:after_create回调,但这非常痛苦。

User.rb:

class User < ActiveRecord::Base
  devise :database_authenticatable, :registerable,
         :recoverable, :trackable, :validatable,
         :confirmable, :rememberable

    has_many :things
    belongs_to :city
    validates_presence_of :city_id
  validates :email, :presence => true, :email => true

  after_create :subscribe


  def subscribe(frequency = :weekly)
    if [:weekly, :monthly].include? frequency
      response = Rails.configuration.mailchimp.lists.subscribe({
        id:           get_list_id(frequency),
        email:        { email: email },
        merge_vars:   { HASH: hashify(frequency), USER_ID: id }, # generate approptiate hash
        double_optin: false
      })
      # response
    end
    update_attributes(newsletter_frequency: frequency.to_s)
    response
  end
end

3 个答案:

答案 0 :(得分:1)

已经4个月了,我不知道你是否已经解决了这个问题,但我仍然会回答这个问题。

我昨天遇到了这个问题,我使用的是DeviseRails版本。首先,我读取控制台日志,找到创建用户的时间,确认令牌有效,然后重写。

调试后,我在update中发现问题为after create。为什么?我发现update语句还添加了unconfirmed_email属性,这意味着在更新时,电子邮件会发生变化。这很奇怪,因为我没有更新电子邮件专栏。所以我在更新语句

之前添加以下代码
changed_attributes.each do |attr, origin|
  puts "#{attr}: #{origin.inspect} => #{self.send(attr)}"
end

结果是:

id: nil => 108
email: "" => d33faf@qq.com

请参阅?更新时,电子邮件地址会更改,甚至创建id,看起来还没有创建记录。这很奇怪,因为该记录应该被创建,因为它被称为after create

所有关于transactioncreate操作都包含在整个事务中。 after create仍然在此事务中,并且由于insert语句尚未提交,因此Rails认为数据库中的电子邮件为nil且电子邮件已更改。

所以到最后,更新会在Devise中触发以下回调:

before_update :postpone_email_change_until_confirmation_and_regenerate_confirmation_token, if: :postpone_email_change?

然后它发现电子邮件已更改,因此它会重新生成确认令牌。

好的,我们怎么能避免呢?

最简单的方法是使用update_columns代替update,这不会触发before_update回调,但会绕过验证。

更好的方法是使用after_commit on: :create而不是after_create,因为它将在事务后执行。但问题是目前它有一个bug。请参阅我在Github上创建的问题:https://github.com/rails/rails/issues/16286以及关于SO的问题:Prevent infinite loop when updating attributes within after_commit, :on => :create

答案 1 :(得分:0)

我认为这篇SO帖子可能会引导你朝着正确的方向前进。 Devise "Confirmation token is invalid" when user signs up

特别是此博文http://blog.plataformatec.com.br/2013/08/devise-3-1-now-with-more-secure-defaults/

答案 2 :(得分:0)

你需要使用'after_confirmation'方法而不是使用after_create回调。

def after_confirmation
  #your code here  
end