5

我的用户模型中有这段代码:

class User < ActiveRecord::Base
  attr_accessible :email, :password, :password_confirmation
  attr_accessor :password

  before_save :encrypt_password

  validates :email, :presence => true,
                    :uniqueness => { :case_sensitive => false },
                    :format => { :with => /\A[^@]+@[^@]+\z/ },
                    :length => 7..128
  validates :password, :presence => true,
                       :confirmation => true,
                       :length => 6..128

  private
    def encrypt_password
      return unless password
      self.encrypted_password = BCrypt::Password.create(password)
    end
end

现在在我的控制器中,当我更新一些用户字段时

@user.update_attributes(params[:user])

始终验证密码字段,即使未在 params 哈希中设置它也是如此。我认为这是因为 attr_accesor :password 总是在 update_attributes 上设置 password = ""。

现在我可以简单地跳过密码验证,如果它是一个空字符串:

validates :password, :presence => true,
                     :confirmation => true,
                     :length => 6..128,
                     :if => "password.present?"

但这不起作用,因为它允许用户设置空密码。

在我想更改的字段上使用 update_attribute 不是解决方案,因为我需要对该属性进行验证。如果我传入确切的参数

@user.update_attributes(params[:user][:fieldname])

它不能解决问题,因为它还会触发密码验证。

没有办法阻止 attr_accesor :password 在更新时始终设置 password = "" 吗?

4

2 回答 2

14

新答案

这对我有用:

validates :password, :presence     => true,
                     :confirmation => true,
                     :length       => { :minimum => 6 },
                     :if           => :password # only validate if password changed!

如果我没记错的话,我也花了一些时间才能做到这一点(大量的试验和错误)。我从来没有时间确切地找出它为什么有效(与 相比:if => "password.present?")。

旧答案 - 对您的目的不是很有用(请参阅评论) 我通过使用完全不同的密码更新操作(user#update_password)来解决这个问题。现在只验证密码字段就足够了

:on => [:create, :update_password]

(并且也只能让这些操作可以访问)。

这里有更多细节:

在您的路线中:

resources :users do
  member do
    GET :edit_password # for the user#edit_password action
    PUT :update_password # for the user#update_passwor action
  end
end

在您的用户控制器中:

def edit_password
  # could be same content as #edit action, e.g.
  @user = User.find(params[:id])
end
def update_password
  # code to update password (and only password) here
end

在您的 edit_password 视图中,您现在有一个仅用于更新密码的表单,与您在编辑视图中的表单非常相似,但使用 :method => :put 和 :url => edit_password_user_path(@user)

于 2011-11-17T16:34:26.627 回答
2

我开始用来解决这个问题的解决方案是:

开始使用 ActiveModel 的内置has_secure_password方法。

在控制台

rails g migration add_password_digest_to_users password_digest:string
rake db:migrate

在您的模型中:

class User < ActiveRecord::Base
  has_secure_password

  attr_accessible :login_name, :password, :password_confirmation

  # secure_password.rb already checks for presence of :password_digest
  # so we can assume that a password is present if that validation passes
  # and thus, we don't need to explicitly check for presence of password
  validates :password, 
    :length => { :minimum => 6 }, :if => :password_digest_changed?

  # secure_password.rb also checks for confirmation of :password 
  # but we also have to check for presence of :password_confirmation
  validates :password_confirmation, 
    :presence=>true, :if => :password_digest_changed?
end

最后,

# In `config/locales/en.yml` make sure that errors on
# the password_digest field refer to "Password" as it's more human friendly 

en:
  hello: "Hello world"

  activerecord:
    attributes:
      user:
        password_digest: "Password"  

哦,还有一件事:观看railscast

于 2011-11-17T16:29:46.133 回答