设计没有密码的更新用户


77

我想在设计时更新没有密码的用户属性。情况是这样的,如果密码和密码确认字段不为空,那么我需要设计错误,如果它们为空,则需要更新其他用户属性。我怎样才能通过设计做到这一点?

提前致谢!

Answers:


64

这是您要找的东西吗?来自Devise Wiki

允许用户编辑其帐户而无需提供密码

它显示了如何在导轨3和导轨4上进行操作

=======================

约翰的解决方案是一个更简单的选择


1
此后,何塞·瓦利姆(JoséValim)删除了该Wiki上的许多信息,建议用户编写自己的控制器,而不要覆盖继承的Devise::RegistrationsControllers中的方法。可以查看以前版本的Wiki历史记录,但可以考虑他的建议。
安倍·沃克

93

我认为这是一个更好的解决方案:

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

这可以防止您仅通过从表单响应中删除密码字段(如果为空)来更改Devise控制器。

只需确保在操作之前 @user.attributes = params[:user]或使用任何update操作从表单中设置新参数时使用它即可。


完善。正是我要找的东西
karlingen 2014年

4
你把它放在哪里?
亚历山大·普雷斯伯2014年

5
@MoMolog遵循Devise教程的第一部分:github.com/plataformatec/devise/wiki / ... 只需创建一个空白的自定义注册控制器。完成此操作后,可从此处复制标准Devise注册控制器上的更新功能:github.com/plataformatec/devise/blob/master/app/controllers/… 将John的代码放在控制器顶部。用“ @ user.update_attributes(account_update_params)”替换“ update_resource(resource,account_update_params)”
onichase

可以放在模型中吗?@onichase
ahnbizcad 2014年

4
不考虑此解决方案current_password。它设计用于更改密码字段还显示所有用户属性(名称,电子邮件等)的情况。没有这样的逻辑,如果用户提交的表单更改了其他字段,而密码字段为空,则Devise将不允许更新任何属性。
约翰

18

我认为使用Devise 3.2+,您可以在子类控制器中重写update_resource。这是最初询问的问题的示例:

class MyRegistrationsController < Devise::RegistrationsController
  protected

  def update_resource(resource, params)
    resource.update_without_password(params)
  end
end

这个规则统治着所有人,这是迄今为止最简单的规则,截至2015
。– Asciant

3
只要确保并更新您的路线即可:devise_for :users, controllers: {registrations: 'registrations'}
Dylan Little

好的,但我还必须添加attr_accessor :current_password到我的用户模型中。我不喜欢这种骇客。我是不是该?
masciugo,2015年

您只需在params.delete :current_password之前添加resource.update_without_password(params)
sebsonic2o


8

我解决了以下问题:如果表单使用密码提交,则需要填写current_password字段或不使用密码和current_password进行更新的表单

在模型中:

class User
  devise: bla-bla-bla

  attr_accessor :current_password
end

在控制器中:

class Users::RegistrationsController <  Devise::RegistrationsController
  layout 'application'


   def update
    self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)


    # custom logic
    if params[:user][:password].present?
      result = resource.update_with_password(params[resource_name])
    else
      result = resource.update_without_password(params[resource_name])
    end

    # standart devise behaviour
    if result
      if is_navigational_format?
        if resource.respond_to?(:pending_reconfirmation?) && resource.pending_reconfirmation?
          flash_key = :update_needs_confirmation
        end
        set_flash_message :notice, flash_key || :updated
      end
      sign_in resource_name, resource, :bypass => true
      respond_with resource, :location => after_update_path_for(resource)
    else
      clean_up_passwords resource
      respond_with resource
    end
  end
end

1
这个答案是最完整的,解决方案是最好的
Diego Somar

4

params [:user] [:password]在rails 6中不起作用

您必须将params [:user] [:password]更改为params [:password]

在所有参数中删除[:user]

我的源代码如下

在运行此命令之前

Rails生成devise:controllers用户-c = registrations

class Users::RegistrationsController < Devise::RegistrationsController
  # before_action :configure_sign_up_params, only: [:create]
  # before_action :configure_account_update_params, only: [:update]

  protected

  def update_resource(resource, params)
    puts "params ===> #{params}"
    if params[:password].blank? && params[:password_confirmation].blank?
      params.delete(:password)
      params.delete(:password_confirmation)
      params.delete(:current_password)
      resource.update_without_password(params)
    else
      super
    end
  end
end

3

我用我的界面解决了这个问题。它可以有两种方法。

如果字段为空,请禁用

jQuery的这一位在字段为空时会在表单提交之前禁用这些字段。它需要某种标记模式,请在Codepen上查看演示

$(".password-fields").each(function() {
  var $fields, $form;
  $form = $(this).parents("form");
  $fields = $(this).find("input");
  $form.on("submit", function() {
    $fields.each(function() {
      if ($(this).val() === "") {
        $(this).attr("disabled", "disabled");
      }
    });
  });
});

给用户一个复选框,以选择是否要更新

另一个选择是,如果用户未指示他们要更新密码,则从表单中删除密码字段。这是CodePen上的示例。

$(".password-fields").each(function () {
  var $fields, $button, html;
  $fields = $(this);
  $button = $fields.prev(".update-password").find("input");
  html = $fields.html();

  $button
    .on("change", function () {
      if ( $(this).is(":checked") ) {
        $fields.html(html);
      }
      else {
        $fields.html("");
      }
    })
    .prop("checked", "checked")
    .click();
});

无论哪种情况,都不需要更新应用程序本身。您只是在提交要更改的字段。


3

我改写了#password_required?用户模型中的方法。

class User < ActiveRecord::Base
  devise :database_authenticatable, :validatable #, ...

  def password_required?
    if respond_to?(:reset_password_token)
      return true if reset_password_token.present?
    end
    return true if new_record?
    password.present? || password_confirmation.present?
  end
end

因此,如果用户是新用户,则将需要指定密码。但是,仅当存在用户填写password或password_confirmation属性时,才需要指定密码。

有关更多详细信息,请参见:https : //github.com/plataformatec/devise/blob/master/lib/devise/models/validatable.rb#L33

我的实现与原始实现几乎相同:https : //github.com/plataformatec/devise/blob/master/lib/devise/models/validatable.rb#L53

除了我检查是否存在(对于空白字符串,它返回false)

这是关于我在此问题上的拉取请求的讨论:https : //github.com/plataformatec/devise/pull/3920


3

如果您仍然希望支持密码更改但将其设置为可选,请检查current_password诸如此类的可用性:

class MyRegistrationsController < Devise::RegistrationsController
  protected

  def update_resource(resource, params)
    if params[:current_password].blank?
     resource.update_without_password(params.except(:current_password))
    else
      resource.update_with_password(params)
    end
  end
end

这样,如果存在current_password,则可以继续并更新密码,否则将其忽略并不使用密码进行更新。


1

如果您希望Devise仅在用户尝试更改密码时检查当前密码(这意味着您可以在不提供当前密码的情况下更改其他属性):

class RegistrationsController < Devise::RegistrationsController

  protected

    def update_resource(resource, params)
      if params[:password].blank? && params[:password_confirmation].blank?
      resource.update_without_password(params)
    else
     super
    end
  end
end

同样在您的模型中:

attr_accessor :current_password

而且不要忘记

devise_for :users, controllers: {registrations: 'registrations'}

routes.rb中


1

这是更多的业务逻辑,因此我不会在控制器中放入太多代码。我建议Devise::Models::Validatable#password_required?您在模型中进行覆盖:

def password_required?
  new_record? || password.present? || password_confirmation.present?
end

到目前为止,这是执行此操作的最佳方法。也是最可定制的,因为可以准确地指定要求密码的属性。
瓦迪姆

1

解决方法放在用户模型中

def password_required?
  encrypted_password.blank? || encrypted_password_changed?
end

1

对于未来的Google员工,我只花了3个小时就可以解决此问题。

在我的用户模型中,仅当密码和password_confirmation存在时,才删除:validatable并添加validates方法:

class User < ApplicationRecord
  devise :database_authenticatable, :registranable, recoverable, :rememberable
    
  validates :password, length: { in: 6..128 }, if: lambda {self.password.present?}
  validates_confirmation_of :password, if: lambda {self.password.present?}
end

然后在我的users_controller的更新方法中,如果密码和password_confirmation参数为空,则将其删除

class UsersController > ApplicationController
  def update
    if params[:user][:password].blank?
      params[:user].delete(:password)
      params[:user].delete(:password_confirmation)
    end

    respond_to do |format|
      if @user.update(user_params)
        format.html { redirect_to @user, notice: 'User was successfully updated' }
      else
        format.html { render :edit }
      end
    end
  end
end

简单,简单,轻松并且不会因Devise复杂的处理方式而发生变化。

不客气


1

在2020年访问的任何人,这是我发现的最简单的解决方案:

registrations_controller.rb

class RegistrationsController < Devise::RegistrationsController

  protected

  def update_resource(resource, params)
    # Require current password if user is trying to change password.
    return super if params["password"]&.present?

    # Allows user to update registration information without password.
    resource.update_without_password(params.except("current_password"))
  end
end

在routes.rb中:

devise_for :users, controllers: {
  registrations: 'users/registrations'
}

完全归功于:Nishiguchi Masatoshi

https://www.mnishiguchi.com/2017/11/24/rails-devise-edit-account-without-password/


0

我遇到了完全相同的问题,这是我想出的解决方案,并且相信我可以使用。我所做的是创建第二种user_params方法并以user_params_no_pass任何方式命名它:发生的情况是,当密码需要更新时,管理员将提供密码,否则将密码留空。如果密码为空,user_params_no_pass则使用其他密码user_params。希望对您有所帮助

    def update

    if @user.valid_password?(params[:user][:password])
          respond_to do |format|

            if @user.update(user_params)
              format.html { redirect_to @user, notice: 'User profile was successfully updated.' }
              format.json { render :show, status: :ok, location: @user }
            else
             format.html { render :new }
             format.json { render json: @user.errors, status: :unprocessable_entity }
            end
          end
    else
      respond_to do |format|

            if @user.update(user_params_no_pass)
              format.html { redirect_to @user, notice: 'User profile was successfully updated without password.' }
              format.json { render :show, status: :ok, location: @user }
            else
              format.html { render :edit }
              format.json { render json: @user.errors, status: :unprocessable_entity }
            end
          end
    end
  end

  def destroy
     @user.destroy
      respond_to do |format|
        format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
        format.json { head :no_content }
      end
  end
  private

    def set_user
      @user = User.find(params[:id])
    end

    def user_params
      params.require(:user).permit(:user_name, :first_name, :middle_name, :last_name, :dob, :gender, :race, :hispanic, :leader, :mentor, :student, :email, :organization_id, :password, :opus,
  :release_date, :days_to_release, :current_level, :is_active)
    end


    def user_params_no_pass
      params.require(:user).permit(:user_name, :first_name, :middle_name, :last_name, :dob, :gender, :race, :hispanic, :leader, :mentor, :student, :email, :organization_id, :opus,
  :release_date, :days_to_release, :current_level, :is_active)
    end

0

我发现上面代码的这种细微变化更容易理解:

def update
  @user = User.find(params[:id])

  method = if user_params[:password].blank?
             :update_without_password
           else
             :update_with_password

  if @user.send(method, user_params)
    redirect_to @user, notice: 'User settings were saved.'
  else
    render :edit
  end
end

0

经过对上述可能性的大量探索,我终于找到了一种解决方案,该解决方案使您无需密码即可更新某些属性,而有些则需要:

我用以下格式查看了user / edit.html.erb的视图。

<%= form_for(@user) do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <%= f.label :name %>
  <%= f.text_field :name, class: 'form-control' %>

  <%= f.submit "Save changes"%>
<% end %>

我在routes.rb中设置了路由

resources :users, only: [:show, :index, :edit, :update]

我在users_controller.rb中做了编辑和更新方法

def edit
  @user = current_user
end

def update
  @user = current_user
  if @user.update_attributes(user_params)
    flash[:success] = "Profile updated"
    redirect_to root_url
  else
    render 'edit'
  end
end


def user_params
  params.require(:user).permit(:name, :avatar, :whatsup)
end

我使用此编辑视图进行不需要密码的更改。它完全跳过了设计注册控制器,因为我与之链接

edit_user_path(current_user)

我还设置了参数,以便在此处不能更改电子邮件和密码。要更新密码和电子邮件,我链接到库存生成的设备视图:

edit_user_registration_path(current_user)

我承认这是一个很大的工作,但是没有一个简单的解决方案可以解决所有上述问题。


0

更好,更短的方法:将检查参数添加到user_params块中。例如:

def user_params
    up = params.require(:user).permit(
      %i[first_name last_name role_id email encrypted_password
         reset_password_token reset_password_sent_at remember_created_at
         password password_confirmation]
    )

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

0

要更新用户的属性,您将需要使用:current_password,以选中“您的用户使用了正确的current_password还是有人想破坏您的用户”?

形式如下:

= f.input :current_password,
          hint: "we need your current password to confirm your changes",
          required: true,
          input_html: { autocomplete: "current-password" }

在控制器中:

    if your_user.valid_password?(params[:user][:current_password])
        your_user.update_attributes(user_params.except(:current_password, :password, :password_confirmation))
    end

第一行检查“您的用户是否发送了正确的密码”,然后我们就可以更新属性而不会产生垃圾

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.