无需密码即可更新“用户”属性

发布于 2024-11-30 01:33:26 字数 1097 浏览 1 评论 0原文

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

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 

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

Right now, users can edit some their attributes without having to enter their password because my validations are set up like this:

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? } 

However, after a user does this, their password is deleted - update_attributes is updating their password to "". Here is my update definition:

def update

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

I've also tried using a different definition that uses update_attribute instead:

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 

But for some reason this is doing the same thing. How can I update some user attributes without changing the password? Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

烦人精 2024-12-07 01:33:26

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

好吧,汲取灵感 从 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

I didn't realize the solution I gave you yesterday would lead to this problem. Sorry.

Well, taking inspiration from devise, you should simply update your controller this way:

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
青柠芒果 2024-12-07 01:33:26

这篇博文演示了您想要做的事情的原理。

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

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?

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

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

This blog post demonstrates the principal of what you want to do.

What is not shown, but may be helpful, is to add accessors to the model:

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

and to provide all of the desired validation under the condition that the user has provided a new password.

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

Lastly, I would add a check to see if the encrypted_password has been set in order to determine if "password_changed?" in order to require a password on a new record.

  def password_changed?
    !@new_password.blank? or encrypted_password.blank?
  end
浅浅淡淡 2024-12-07 01:33:26

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

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

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

步骤 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 - 如果未设置密码则跳过检查。

I've been struggling with this and going around in circles for a while, so I thought I'd put my Rails 4 solution here.

None of the answers I've seen so far meet my use case, they all seem to involve bypassing validation in some way, but I want to be able to validate the other fields and also the password (if present). Also I'm not using devise on my project so i can't make use of anything particular to that.

Worth pointing out that it's a 2 part problem:

Step 1 - you need to remove the password and confirmation field from the strong parameters if the password is blank like so in your controller:

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

Step 2 - you need to alter validation such that the password isn't validated if it's not entered. What we don't want is for it to be set to blank, hence why we removed it from our parameters earlier.

In my case this means having this as the validation in my model:

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

Note the :if => :password - skip checking if the password is not being set.

巨坚强 2024-12-07 01:33:26
# 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
# 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
淡莣 2024-12-07 01:33:26

2017 年答案:

Rails 5 中,Michael Hartl 的 教程,您在模型中获得以下内容就足够了:

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

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

此时人们可能会认为这也将允许空用户注册;但是,可以通过使用 仅在 create 方法中自动验证密码是否存在

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

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 answer:

In Rails 5 as also pointed out by Michael Hartl's tutorial, it's enought that you get something along these lines in your model:

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

allow_nil: true is the key here. This allows a user to edit his/her info without expecting to also change the password.

At this point people may think that this will also allow empty user signups; However this is prevented by using the has_secure_password which automatically validates password presence exclusively within the create method.

This is a demo User model for illustration purposes:

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

Unfortunately I've no clue how to make this work in devise.
Cheers ????.

离不开的别离 2024-12-07 01:33:26

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

before_save :encrypt_password

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

before_save :encrypt_password, :unless => Proc.new { |u| u.密码.空白? }

I had the same problem, and the solutions above didn't work for me. I found the real culprit in my case: I had an encrypt_password callback in my User model, which was setting the password to blank each time.

before_save :encrypt_password

I fixed it by adding a condition at the end for this call back:

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

你与清晨阳光 2024-12-07 01:33:26

正确答案不再适用于 Rails 4。我相信我的答案是最干净且最通用的,每当您想省略任何属性(不仅仅是密码)时,它都会起作用。如果您想在多个不同位置更新任何模型的单独属性,则需要这种方法。

例如,如果您想要执行 Stack Overflow 所做的操作,并通过安全页面更新密码、通过用户显示视图更新个人资料图像以及通过用户编辑更新大量用户信息看法。

1) 使用类方法扩展哈希类以删除空白值。我们将使用此方法删除未更新但仍存在于 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 中:

config/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!类方法来删除我们不从 params 哈希中更新的属性。然后,通过 update_attributes 方法更新用户实例,*不是 update 方法!

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

app/controllers/users_controller.rb

new_params = user_params.delete_blanks!

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

app/controllers/users_controller.rb

@user.update_attributes(new_params)

这是完成的 users#update 操作的样子看看:

app/controllers/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> 选项。这是为了确保仅当属性存在于 params 哈希中时才会触发验证。我们的 delete_blanks! 方法将从 params 哈希中删除该属性,因此不会运行密码验证等操作。还值得注意的是,delete_blanks! 仅删除值为 nil 的哈希条目,而不是那些空字符串的哈希条目。因此,如果有人在用户创建表单(或任何带有密码字段的表单)上遗漏了密码,则存在验证将生效,因为哈希的 :password 条目不会为零,它将是一个空string:

3a) 在所有验证中使用 if: 选项:

app/models/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)

The correct answer no-longer works for rails 4. I believe my answer is the cleanest and the most versatile that will work whenever you want to leave out any attributes (not just the password). This approach will be needed if you want to update the separate attributes of any model in a number of different places.

For example, if you want to do what Stack Overflow does and have the passwords updatable via a security page, the profile image updatable via the user show view and the bulk of a user's information updatable via a user edit view.

1) Extend the hash class with a class method to delete blank values. We will use this method to remove blank values that are not being updated but are still present in the params hash:

1a) Create a hash.rb file in your lib directory, under an ext directory:

command line

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

1b) Inside hash.rb, 'create' a Hash class and create a .delete_blanks! method:

lib/ext/hash.rb

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

1c) Require this file (and your entire lib directory) into the rails referencing it in an initializer:

config/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) Inside the users#update action, implement our shiny new delete_blanks! class method to remove the attributes we're not updating from the params hash. Then, update the user instance via the update_attributes method, *not the update method!

2a) Firstly, let's use the delete_blanks! method to fix our user_params hash:

app/controllers/users_controller.rb

new_params = user_params.delete_blanks!

2b) And now let's update the instance using the update_attributes method, (again, not the update method):

app/controllers/users_controller.rb

@user.update_attributes(new_params)

Here's how the finished users#update action should look:

app/controllers/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) In the User model, add the if: :<attribute> option to all of your validations. This is to make sure the validation is only triggered if the attribute is present in the params hash. Our delete_blanks! method will have removed the attribute from the params hash, so the validation for password, for example, won't be run. It's also worth noting that delete_blanks! only removes hash entries with a value of nil, not those with empty strings. So if someone leaves out the password on the user create form (or any form with a field for the password), a presence validation will take effect because the :password entry of the hash won't be nil, it'll be an empty string:

3a) Use the if: option on all validations:

app/models/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

And that's it. Now the user model can be updated over many, many different forms all over your app. Presence validations for an attribute still come into play on any form that contains a field for it, e.g. the password presence validation still would come into play in the user#create view.

This may seem more verbose than other answers, but I believe this is the most robust way. You can update in isolation an infinite number of attributes for User instances, on an infinite amount of different models. Just remember when you want to do this with a new model you need to repeat the steps 2a), 2b) and 3a)

梦里的微风 2024-12-07 01:33:26
@user.username=params[:username]
if @user.update_attribute(:email,params[:email])

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

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

@user.username=params[:username]
if @user.update_attribute(:email,params[:email])

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

above code can update username and email field.
because update_attribute can update dirty fields.
but it is a pity, update_attribute would skip validation.

相守太难 2024-12-07 01:33:26

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

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

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

I was having the same problem. I wasn't able to fix it with

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

I have only been able to get it to work by doing "update_attribute" on each item individually, e.g.

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

which is clearly a total hack but its the only way I can figure it out without messing with the validations and deleting the password!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文