Rails验证当密码存在或已更改时,password_confirmation也存在。

7

我有以下用户模型:

class User < ActiveRecord::Base
  # Users table has the necessary password_digest field
  has_secure_password
  attr_accessible :login_name, :password, :password_confirmation

  validates :login_name, :presence=>true, :uniqueness=>true

  # I run this validation on :create so that user 
  # can edit login_name without having to enter password      
  validates :password,:presence=>true,:length=>{:minimum=>6},:on=>:create

  # this should only run if the password has changed
  validates :password_confirmation, 
            :presence=>true, :if => :password_digest_changed?
end

这些验证并没有完全达到我的期望。以下操作是可能的:
# rails console
u = User.new :login_name=>"stephen"
u.valid? 
# => false
u.errors 
# => :password_digest=>["can't be blank"], 
# => :password=>["can't be blank", "please enter at least 6 characters"]}

# so far so good, let's give it a password and a valid confirmation
u.password="password"
u.password_confirmation="password"

# at this point the record is valid and does save
u.save
# => true

# but we can now submit a blank password from console
u.password=""
# => true
u.password_confirmation=""
# => true

u.save
# => true
# oh noes

我希望您能做以下事情:
  • 创建时需要密码,密码长度必须为6个字符
  • 在创建时需要确认密码,确认密码必须与密码匹配
  • 用户在更新登录名时不必提交密码
  • 密码不能在更新时被删除
有一件令我困惑的事情是,如果我在password_confirmation验证中使用password_changed?而不是:password_digest_changed?,Rails会抛出一个“no method”错误。我不明白为什么。
请问有人知道我做错了什么吗?
1个回答

14

password 不是数据库中的列,对吧?只是一个属性?

因此,如果password 是一个列,就会有可用的password_changed? 方法。但实际上并没有这个方法,所以您应该检查是否设置了password

类似这样:

validates :password_confirmation, :presence => true, :if => '!password.nil?'

虽然这解决了你最初遇到的问题,但它仍不能完全做到你想要的,因为它只检查存在性,而你需要它既存在又匹配密码。以下代码应该可以实现(与上述验证结合使用)。

validates :password, 
          # you only need presence on create
          :presence => { :on => :create },
          # allow_nil for length (presence will handle it on create)
          :length   => { :minimum => 6, :allow_nil => true },
          # and use confirmation to ensure they always match
          :confirmation => true

如果你之前从未见过:confirmation,它是一种标准验证方式,会检查foofoo_confirmation是否相同。

请注意,你仍需要检查password_confirmation是否存在。


啊,你说得对,password和password_confirmation不是数据库字段,这就解释了为什么我无法访问它们的dirty方法。用户仍然可以在更新时删除他们的密码,并且可以将其缩短到少于6个字符。这是我的主要问题。 - stephenmurdoch
我已经编辑过了,以解决您的其他问题。看起来应该可以工作。 - numbers1311407
3
谢谢,你的代码非常接近我最终使用的代码。最后,我能够删除 :presence 验证,因为 secure_password 已经检查了 :password_digest 的存在。我制作了一个gist以便将来提醒自己。 - stephenmurdoch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接