无需密码即可更新`User`属性

现在,用户可以编辑一些属性,而无需输入密码,因为我的validation设置如下:

validates :password, :presence =>true, :confirmation => true, :length => { :within => 6..40 }, :on => :create validates :password, :confirmation => true, :length => { :within => 6..40 }, :on => :update, :unless => lambda{ |user| user.password.blank? } 

但是,在用户执行此操作后,将删除其密码 – update_attributes将其密码更新为“”。 这是我的更新定义:

 def update if @user.update_attributes(params[:user]) flash[:success] = "Edit Successful." redirect_to @user else @title = "Edit user" render 'edit' end end 

我也尝试使用另一种使用update_attribute的定义:

 def save_ff @user = User.find(params[:id]) @user.update_attribute(:course1, params[:user][:course1] ) @user.update_attribute(:course2, params[:user][:course2] ) @user.update_attribute(:course3, params[:user][:course3] ) @user.update_attribute(:course4, params[:user][:course4] ) redirect_to @user end 

但出于某种原因,这是做同样的事情。 如何在不更改密码的情况下更新某些用户属性? 谢谢!

我没有意识到我昨天给你的解决方案会导致这个问题。 抱歉。

好吧, 从设计中获取灵感,您应该以这种方式更新控制器:

 def update params[:user].delete(:password) if params[:user][:password].blank? if @user.update_attributes(params[:user]) flash[:success] = "Edit Successful." redirect_to @user else @title = "Edit user" render 'edit' end end 

这篇博客文章展示了你想要做的事情的原则。

未显示但可能有用的是向模型添加访问器:

 attr_accessor :new_password, :new_password_confirmation attr_accessible :email, :new_password, :new_password_confirmation 

并在用户提供新密码的条件下提供所有所需的validation。

  validates :new_password, :presence => true, :length => { :within => 6..40 }, :confirmation => true, :if => :password_changed? 

最后,我会添加一个检查,看看是否已设置encrypted_pa​​ssword以确定是否“password_changed?” 为了在新记录上要求密码。

  def password_changed? !@new_password.blank? or encrypted_password.blank? end 

我一直在努力解决这个问题并绕圈子走了一段时间,所以我想我会把我的Rails 4解决方案放在这里。

到目前为止我看到的答案都没有达到我的用例,它们似乎都涉及以某种方式绕过validation,但我希望能够validation其他字段以及密码(如果存在)。 此外,我没有在我的项目中使用设计,所以我无法利用任何特定的东西。

值得指出的是,这是一个2部分的问题:

步骤1 – 如果密码在控制器中为空,则需要从强参数中删除密码和确认字段:

 if myparams[:password].blank? myparams.delete(:password) myparams.delete(:password_confirmation) end 

第2步 – 您需要更改validation,以便在未输入密码时validation密码。 我们不想要的是将它设置为空白,因此我们之前为什么要从参数中删除它。

在我的情况下,这意味着在我的模型中将其作为validation:

 validates :password, :presence => true, :confirmation => true, length: {minimum: 7}, :if => :password 

注意:if =>:password – 跳过检查是否未设置密码。

 # It smells def update if params[:user][:password].blank? params[:user].delete :password params[:user].delete :password_confirmation end if @user.update_attributes(params[:user]) flash[:success] = "Edit Successful." redirect_to @user else @title = "Edit user" render 'edit' end end # Refactoring class User < ActiveRecord::Base ... def update_attributes(params) if params[:password].blank? params.delete :password params.delete :password_confirmation super params end end ... end def update if @user.update_attributes(params[:user]) flash[:success] = "Edit Successful." redirect_to @user else @title = "Edit user" render 'edit' end end # And little better class User < ActiveRecord::Base ... def custom_update_attributes(params) if params[:password].blank? params.delete :password params.delete :password_confirmation update_attributes params end end ... end def update if @user.custom_update_attributes(params[:user]) flash[:success] = "Edit Successful." redirect_to @user else @title = "Edit user" render 'edit' end end 

我有同样的问题,上面的解决方案对我不起作用。 我在我的案例中找到了真正的罪魁祸首:我的用户模型中有一个encrypt_password回调,每次都将密码设置为空白。

before_save:encrypt_password

我通过在此回调的末尾添加条件来修复它:

before_save:encrypt_password,:unless => Proc.new {| u | u.password.blank? }

2017答案:

在迈克尔·哈特尔(Michael Hartl)的教程中也指出了Rails 5中,你的模型中有这样的东西:

 validates :password, presence: true, length: { minimum: 6 }, allow_nil: true 

allow_nil:true是这里的关键,它允许用户编辑他/她的信息,而不需要更改密码。

在这一点上,人们可能会认为这也将允许空用户注册; 但是,通过使用has_secure_password可以防止这种情况,该has_secure_password会自动validation密码存在,但只会使用create方法。

这是一个演示用户模型,用于说明目的:

 class User < ApplicationRecord attr_accessor :remember_token before_save { self.email = email.downcase } validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[az\d\-.]+\.[az]+\z/i validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false } has_secure_password validates :password, presence: true, length: { minimum: 6 }, allow_nil: true . . . end 

我不知道如何用设计做到这一点。 我的两分钱。

正确答案不再适用于导轨4 。 我相信我的答案是最干净, 最通用的 ,每当你想要遗漏任何属性(而不仅仅是密码)时都会有效。 如果要在多个不同位置更新任何模型的单独属性,则需要使用此方法。

例如,如果要执行Stack Overflow所做的操作并通过security页面更新密码,则可通过用户显示视图更新配置文件图像,并通过用户编辑视图更新用户信息的大部分。

1)使用类方法扩展hash class以删除空值。 我们将使用此方法删除未更新但仍存在于params哈希中的空白值:

1a)lib目录下的ext目录下创建一个hash.rb文件:

命令行

 $ mkdir lib/ext $ touch lib/ext/hash.rb 

1b)hash.rb里面,’创建’一个Hash类并创建一个.delete_blanks! 方法:

LIB / EXT / hash.rb

 class Hash def delete_blanks! delete_if { |k, v| v.nil? } end end 

1c)将此文件(以及整个lib目录)放入在初始化程序中引用它的rails中:

配置/的boot.rb

 # other things such as gemfiles are required here, left out for brevity Dir['lib/**/*.rb'].each { |f| load(f) } # requires all .rb files in the lib directory 

2)在用户#update活动中,实现我们shiny的新delete_blanks! class方法删除我们没有从params哈希更新的属性。 然后,通过update_attributes方法更新用户实例,*而不是update方法!

2a)首先,让我们使用delete_blanks! 修复user_params哈希的方法:

应用程序/控制器/ users_controller.rb

 new_params = user_params.delete_blanks! 

2b)现在让我们使用update_attributes方法更新实例(再次,不是update方法):

应用程序/控制器/ users_controller.rb

 @user.update_attributes(new_params) 

以下是完成的users#update操作应该如何显示:

应用程序/控制器/ users_controller.rb

 def update new_params = user_params.delete_blanks! if @user.update_attributes(new_params) redirect_to @user, notice: 'User was successfully updated.' else render action: 'edit' // or whatever you want to do end end 

3)User模型中,为所有validation添加if: :选项。 这是为了确保只有在params散列中存在属性时才会触发validation。 我们的delete_blanks! 方法将从params哈希中删除该属性,因此不会运行密码validation。 还值得注意的是delete_blanks! 仅删除值为nil的哈希条目,而不是具有空字符串的哈希条目。 因此,如果有人在用户创建表单(或任何带有密码字段的表单)上遗漏了密码,则状态validation将生效,因为哈希的密码条目不会为零,它将为空串:

3a)对所有validation使用if:选项:

应用程序/模型/ user.rb

 VALID_EMAIL_REGEX = /[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9\-.]/ validates :first_name, presence: true, if: :first_name validates :last_name, presence: true, if: :last_name validates :user_name, presence: true, if: :user_name validates :email, presence: true, uniqueness: { case_sensitive: false }, format: { with: VALID_EMAIL_REGEX }, if: :email validates :password, length: { minimum: 6, maximum: 10 }, if: :password 

就是这样。 现在,用户模型可以通过您的应用程序在许多不同的表单上进行更新。 属性的存在validation仍然在包含其字段的任何表单上发挥作用,例如,密码存在validation仍将在user#create视图中发挥作用。

这似乎比其他答案更冗长,但我相信这是最强大的方式。 您可以在无限量的不同模型上单独更新User实例的无限数量的属性。 请记住,当您想要使用新模型执行此操作时,需要重复步骤2a)2b)3a)

 @user.username=params[:username] if @user.update_attribute(:email,params[:email]) flash[:notice]="successful" else flash[:notice]="fail" end 

以上代码可以更新用户名和电子邮件字段 因为update_attribute可以更新脏字段。 但很遗憾,update_attribute会跳过validation。

我遇到了同样的问题。 我无法修复它

 params[:user].delete(:password) if params[:user][:password].blank? 

我只能通过对每个项目单独执行“update_attribute”来使其工作,例如

 if ( @user.update_attribute(:name, params[:user][:name]) && @user.update_attribute(:email, params[:user][:email]) && @user.update_attribute(:avatar, params[:user][:avatar]) && @user.update_attribute(:age, params[:user][:age]) && @user.update_attribute(:location, params[:user][:location]) && @user.update_attribute(:gender, params[:user][:gender]) && @user.update_attribute(:blurb, params[:user][:blurb]) ) flash[:success] = "Edit Successful." redirect_to @user else @title = "Edit user info" render 'edit' end 

这显然是一个彻底的黑客,但它是唯一的方法,我可以搞清楚,而不会弄乱validation和删除密码!