29

现在,用户无需输入密码即可编辑他们的一些属性,因为我的验证设置如下:

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 

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

4

9 回答 9

25

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

好吧,从 devise中获得灵感,您应该以这种方式简单地更新您的控制器:

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
于 2011-08-17T12:23:05.697 回答
12

这篇博文展示了您想要做的事情的主要内容。

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

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

并在用户提供新密码的情况下提供所有所需的验证。

  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
于 2012-03-18T20:45:49.283 回答
11

我一直在为此苦苦挣扎并转圈圈了一段时间,所以我想我应该把我的 Rails 4 解决方案放在这里。

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

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

第 1 步 - 如果密码为空,您需要从强参数中删除密码和确认字段,如下所示:

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

第 2 步 - 您需要更改验证,以便在未输入密码时不验证密码。我们不希望将其设置为空白,因此我们之前将其从参数中删除。

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

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

注意 :if => :password - 如果没有设置密码,则跳过检查。

于 2013-11-22T11:59:36.753 回答
6
# 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
于 2012-06-26T21:11:00.317 回答
4

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

before_save :encrypt_password

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

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

于 2012-01-09T15:35:14.577 回答
4

2017年回答:

Rails 5中,正如 Michael Hartl 的教程所指出的那样,您的模型中包含以下内容就足够了:

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

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

在这一点上,人们可能会认为这也将允许空用户注册;但是,这可以通过使用has_secure_password自动验证密码存在但仅验证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+\-.]+@[a-z\d\-.]+\.[a-z]+\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

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

于 2017-07-26T13:59:49.937 回答
3

正确答案不再适用于 rails 4。我相信我的答案是最简洁和最通用 的,只要您想省略任何属性(而不仅仅是密码),它就会起作用。如果您想在多个不同位置更新任何模型的单独属性,则需要这种方法。

例如,如果您想做 Stack Overflow 所做的事情并通过security页面更新密码,则可以通过用户显示视图更新个人资料图像,并通过用户编辑视图更新大部分用户信息。

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

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

命令行

$ 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) 在 users#update 操作中,实现我们闪亮的新 delete_blanks!类方法从参数哈希中删除我们没有更新的属性。然后,通过方法更新用户实例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模型中,将if: :<attribute>选项添加到所有验证。这是为了确保仅当参数哈希中存在该属性时才触发验证。我们的delete_blanks!方法将从 params 哈希中删除该属性,因此例如密码验证将不会运行。还值得注意的是,delete_blanks!仅删除值为nil的哈希条目,而不是具有空字符串的哈希条目。因此,如果有人在用户创建表单(或任何带有密码字段的表单)上遗漏了密码,则存在验证将生效,因为哈希的 :password 条目不会为零,它将为空细绳:

3a)在所有验证中使用该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

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

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

于 2014-04-06T20:58:07.957 回答
0
@user.username=params[:username]
if @user.update_attribute(:email,params[:email])

  flash[:notice]="successful"
else
  flash[:notice]="fail"
end

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

于 2012-11-13T03:38:40.700 回答
-1

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

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

这显然是一个彻头彻尾的黑客攻击,但这是我在不弄乱验证和删除密码的情况下弄清楚的唯一方法!

于 2011-08-17T16:59:00.270 回答