【问题标题】:Rails state_machine transition causes password validation errorRails state_machine 转换导致密码验证错误
【发布时间】:2026-01-02 13:35:01
【问题描述】:

我一直在关注 Michael Hartl 的 rails 教程,并一直在处理他在 Signup confirmation 中建议的扩展,以让用户单击电子邮件链接以激活他们的帐户。为了管理激活,我正在使用 state_machine gem,但是当我尝试激活用户帐户时,出现错误“无法通过 :activate from :pending 转换状态(原因:密码太短(最少 6 个字符) )”。 对我来说,似乎 state_machine 正在实施以更新 state 字段的数据库操作以某种方式导致与用户记录相关联的验证(可能与 has_secure_password 方法相关联)被触发并失败。 一年前,我确实遇到了关于该主题的另一个 * 问题here,但是阅读有关该问题的 cmets 似乎没有解决方案。

我的用户模型 (user.rb) 代码如下(基本上是清单 6.29 中的教程代码加上状态机)。 [抱歉,无法提供链接,因为 * 不允许我提供超过 2 个链接!]

class User < ActiveRecord::Base
  validates :name, presence: true, length: { maximum: 50 } 
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
  validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, 
    uniqueness: { case_sensitive: false }
  before_save { email.downcase! }
  validates :password, length: { minimum: 6, maximum: 50 }
  has_secure_password
  before_create :create_remember_token

  state_machine initial: :pending do
    state :pending, value: 0
    state :active, value: 1

    event :activate do
      transition :pending => :active
    end

    event :deactivate do
      transition :active => :pending
    end 
  end

  def initialize
    super()
  end

我的数据库用户表结构是:

create_table "users", force: true do |t|
t.string   "name"
t.string   "email"
t.datetime "created_at"
t.datetime "updated_at"
t.string   "password_digest"
t.string   "remember_token"
t.boolean  "admin",           default: false
t.string   "token"
t.integer  "state"
end

在 Rails 控制台中,state_machine 代码似乎可以解释数据库中的值,因为我可以获得用户记录以及何时执行 user.can_activate?和 user.can_deactivate?它让我得到了我的期望。但是当我尝试做 user.activate 时!或 user.deactivate!我得到密码太短的错误。关于如何解决这个问题的任何建议?

【问题讨论】:

    标签: ruby-on-rails passwords ruby-on-rails-plugins railstutorial.org


    【解决方案1】:

    我通过更改代码以省去 state_machine 并直接更新数据库中的 state 字段找到了答案。然后我发现用户记录的所有更新导致触发密码验证并出现密码长度错误(因此不是 state_machine 的问题)。 如here 所述,如果密码不是正在更新的属性之一,则解决方案是防止密码验证发生,方法是将上面的“验证”行更改为:

    validates :password, length: { minimum: 6, maximum: 50 }, :if => :validate_password?
    def validate_password?
      password.present? || password_confirmation.present?
    end
    

    一旦更改,state_machine 功能就可以正常工作。

    【讨论】: