【问题标题】:attr_accessor and password validation on update更新时的 attr_accessor 和密码验证
【发布时间】:2011-11-18 07:04:52
【问题描述】:

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

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 = ""?

【问题讨论】:

  • 我不确定您是否想要 :attr_accessor:password

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


【解决方案1】:

新答案

这对我有用:

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

如果我没记错的话,我也花了一些时间才能做到这一点(大量的试验和错误)。我从来没有时间弄清楚为什么会这样(与:if =&gt; "password.present?" 相比)。

旧答案 - 对您的目的不是很有用(参见 cmets) 我通过使用完全不同的密码更新操作(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)

【讨论】:

  • 是否有任何地方可以让我了解更多关于您在那里使用的update_[something] 方法的信息?我猜这是某种即时元方法,但我实际上找不到任何信息,并且已经搜索了大约 4 分钟的文档。它似乎真的很有用,特别是如果您在密码字段上没有脏方法(例如当它不是数据库中的字段时,应该总是如此)。
  • 谢谢,我明白你现在在做什么了,你对密码更新操作有不同的看法
  • 哦,只是注意到你不是提出这个问题的人 - 这让你的评论对我来说有点不同:) 回答你的问题(因为你可能知道怎么做) : 都是自定义方法
  • 嗯,也许我做错了,但我无法像您对 :on =&gt; [:create, :update_password] 所做的那样接受两个操作
  • 对不起 - 我不得不从记忆中回忆解决方案。现在我回到家检查了我的编码 -> 验证仅支持 :update 或 :create for :on。我想我也偶然发现了这个......对我有用的解决方案更加简单。我会在上面更新它。
【解决方案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

【讨论】:

  • 这不能正常工作,因为user.update_attributes(password: "", password_confirmation: "")returns true(但没有传递到数据库)。它实际上并没有将其更改为空密码,但是控制器中的错误处理会中断。
  • 为什么错误处理会中断?当用户提交空白密码时,您真的想要一条错误消息吗?
  • 如果实际上没有任何成功,我不想显示成功消息......这会使用户感到困惑。但是我可以在控制器中实现另一个 if 语句来明确检查空密码。如果 update_attributes 实际上没有更新任何内容,那么它返回 true 似乎并不正确。
猜你喜欢
  • 2018-01-04
  • 2022-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 2011-11-29
相关资源
最近更新 更多