更新时的attr_accessor和密码验证

时间:2011-11-17 16:10:31

标签: ruby-on-rails validation attr-accessor

我的用户模型中包含此代码:

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:密码总是在update_attributes上设置密码=“”。

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

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

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

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

传入确切的参数
@user.update_attributes(params[:user][:fieldname])

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

是否有办法防止attr_accesor:密码总是在更新时设置密码=“”?

2 个答案:

答案 0 :(得分:14)

新答案

这对我有用:

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

如果我没记错的话,我也花了一些时间才能做到这一点(很多试验和错误)。我从来没有时间找出其确切原因(与:if => "password.present?"相反)。

旧答案 - 对您的目的并不实用(见评论) 我通过使用完全不同的密码更新操作(用户#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
在您的UsersController中

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 =&gt; :put和:url =&gt; edit_password_user_path(@user)

答案 1 :(得分: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