【问题标题】:Devise update user without password设计无密码更新用户
【发布时间】:2011-07-04 01:12:53
【问题描述】:

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

提前致谢!

【问题讨论】:

    标签: ruby-on-rails devise


    【解决方案1】:

    如果你真的想在没有 current_password 的情况下更新密码,那么你需要使用用户 reset_password 方法

    @user.reset_password(params[:user][:password], params[:user][:password_confirmation])
    

    以下是适用于所有场景的问题的完整解决方案:

      if params.dig(:user, :password).blank?
        updated = @user.update_without_password(params[:user].to_unsafe_hash)
      else
        if params.dig(:user, :current_password).nil?
          @user.reset_password(params[:user][:password], params[:user][:password_confirmation])
    
          updated = @user.update_without_password(params[:user].to_unsafe_hash)
        else
          updated = @user.update_with_password(params[:user].to_unsafe_hash)
        end
    
        bypass_sign_in(@user)
      end
    

    【讨论】:

      【解决方案2】:

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

      完全归功于:西口雅敏

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

      【讨论】:

        【解决方案3】:

        对于未来的 Google 员工,我只花了 3 个小时在这上面,这对我有用。

        在我的用户模型中,我删除了 :validatable 并添加了密码和密码确认的验证方法,前提是它们存在:

        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 的更新方法中,如果密码和密码确认参数为空,我将删除它们

        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 复杂的做事方式简单、简单、容易,而且不会乱七八糟。

        不客气。

        【讨论】:

          【解决方案4】:

          要更新用户的属性,您需要使用 :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
          

          第一行检查'你的用户是否发送了正确的密码',然后我们可以更新属性没有垃圾

          【讨论】:

            【解决方案5】:

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

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

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

            我的源代码如下

            在运行此命令之前

            rails generate devise:controllers users -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
            

            【讨论】:

              【解决方案6】:

              更好更短的方法:将检查参数添加到 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
              

              【讨论】:

                【解决方案7】:

                在对上述可能性进行了多次探索之后,我终于找到了一个解决方案,它允许您在没有密码的情况下更新一些属性,而另一些则可以:

                我为 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)
                

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

                【讨论】:

                  【解决方案8】:

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

                  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
                  

                  【讨论】:

                    【解决方案9】:

                    如果您仍想支持密码更改但将其设为可选,请检查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 存在,您可以继续并更新密码,否则忽略它并在没有密码的情况下更新。

                    【讨论】:

                      【解决方案10】:

                      作为用户模型中的解决方法

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

                      【讨论】:

                        【解决方案11】:

                        我遇到了完全相同的问题,这是我想出的解决方案,相信我它有效。 我所做的是创建第二个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
                        

                        【讨论】:

                          【解决方案12】:

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

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

                          【讨论】:

                          • 这是迄今为止最好的方法。也是最可定制的,因为可以准确指定需要密码的属性。
                          【解决方案13】:

                          我改为覆盖#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
                          

                          因此,如果用户是新用户,他将需要指定密码。 但是,只有在填写密码或密码确认属性时,才会要求现有用户指定密码。

                          更多详情见: 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

                          【讨论】:

                            【解决方案14】:

                            如果您希望 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中。

                            【讨论】:

                              【解决方案15】:

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

                              如果字段为空,则禁用字段

                              这部分 jQuery 在表单提交之前禁用字段,以防它们为空。它需要一定的标记模式,检查demo on 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");
                                    }
                                  });
                                });
                              });
                              

                              给用户一个复选框来选择他们是否要更新

                              如果用户没有表示要更新密码,另一种选择是从表单中删除密码字段。这是on 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();
                              });
                              

                              在任何一种情况下,都不需要更新应用程序本身。您只是提交要更改的字段。

                              【讨论】:

                                【解决方案16】:

                                这就是你要找的吗?来自设计维基

                                Allow users to edit their account without providing a password

                                它展示了如何为 rails 3 和 rails 4 执行此操作

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

                                John's solution 是一个更简单的替代方案

                                【讨论】:

                                • José Valim 已经在该 wiki 上 deleted a lot of info 建议用户编写自己的控制器,而不是覆盖继承的 Devise::RegistrationsControllers 中的方法。可以查看wiki history 了解以前的版本,但请考虑他的建议。
                                【解决方案17】:

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

                                class MyRegistrationsController < Devise::RegistrationsController
                                  protected
                                
                                  def update_resource(resource, params)
                                    resource.update_without_password(params)
                                  end
                                end
                                

                                【讨论】:

                                • 这个答案统治了所有这些,到目前为止,截至 2015 年最简单。
                                • 确保并更新您的路线:devise_for :users, controllers: {registrations: 'registrations'}
                                • 好吧,但我还必须将attr_accessor :current_password 添加到我的用户模型中。我不喜欢的那种黑客。我应该吗?
                                • 您可以在resource.update_without_password(params)之前简单地添加params.delete :current_password
                                • 这也是官方记录的方式github.com/heartcombo/devise/wiki/…
                                【解决方案18】:

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

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

                                如果密码字段为空,只需从表单响应中删除密码字段,您就不必更改设计控制器。

                                请务必使用此之前 @user.attributes = params[:user] 或您在update 操作中使用的任何内容来设置表单中的新参数。

                                【讨论】:

                                • 完美。正是我想要的
                                • 你把这个放在哪里?
                                • @MoMolog 遵循 Devise 教程的第一部分:github.com/plataformatec/devise/wiki/… 只需创建一个空白的自定义注册控制器。一旦你这样做了,从这里复制标准设计注册控制器上的更新函数:github.com/plataformatec/devise/blob/master/app/controllers/… 将 John 的代码放在控制器的顶部。将“update_resource(resource, account_update_params)”替换为“@user.update_attributes(account_update_params)”
                                • 可以放到模型里面吗? @onichase
                                • 此解决方案不考虑current_password。它专为显示所有用户属性(姓名、电子邮件等)的情况而设计,除了字段用于更改密码。如果没有这样的逻辑,如果用户提交的表单更改了其他字段而密码字段为空,则 Devise 将不允许更新任何属性。
                                【解决方案19】:

                                我解决了这个问题如下:如果表单使用密码提交,则需要填写 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
                                

                                【讨论】:

                                • 这个答案最全,解决方案最好
                                猜你喜欢
                                • 2023-03-22
                                • 2011-12-23
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                相关资源
                                最近更新 更多