Rails + 设计:使用或不使用密码更新用户

Rails + Devise: Update user with or without pasword

有很多相关问题:
- Devise: Update Account without password confirmation
- rails: devise update user without password
- Devise 3 (rails 4) can't update user without password
- 等...

但是当我尝试使用他们的答案时,我从来没有得到我想要的,所以我决定 post 这种问答方式。

情况:
我正在研究 Rails app using devise and I wanted a "Manage Profile form." I wanted one form for updating user information, including email and password, but also first_name, last_name, and other non-devise fields. I only wanted to require the password fields (password and password_confirmation) if the user was trying to change their password. I also wanted to build my form with the form_for 助手,并将用户能够更新的字段列入白名单。而且我还想要来自 devise 的所有方便的错误消息(例如,"Your password must be at least 8 characters." 或任何它们)。

TL;DR:剥离 params 只为你想要的,我称之为 user_params。在处理表单的操作中检查 user_params[:password] 是否为空,如果它尝试用 @user.update_without_password(user_params) 更新你的 @user 模型,如果它没有尝试用 [=20= 更新].如适用更新调用returnsfalse@user.errors持有说明。

我是这样解决这个问题的:
我在 config/routes 文件中定义了一个 resource

resource :profile  

我为我的资源创建了一个控制器,它扩展了设计的经过身份验证的控制器 ProfilesController < AuthenticatedController 以管理配置文件。配置文件控制器包含几个方法 包括 user_params,它主要将 params 过滤到白名单:

def user_params
  accessible = [
    :first_name,
    :last_name,
    :email,
    :password,
    :password_confirmation
  ]
  params.require(:user).permit(accessible)
end

update 处理表格的业务:

def update
  # current_user holds the logged in user
  @user = current_user
  # IF they've left the password field blank, 
  # AND the devise update_without_password method returns true
  # OR IF a full update of user (including password and password_confirmation) returns true
  # THEN re-sign them in to flush their session, and redirect them back to their dashboard, and send a success message.
  # ELSE re-present the edit form they were just on (there's a handy catcher
  # in the edit view script to render the form errors, you can find them on
  # @user.errors)

  if (user_params[:password].blank? && @user.update_without_password(user_params)) || @user.update(user_params)
    sign_in(@user, bypass: true)
    redirect_to '/dashboard', notice: 'Your profile changes have been saved.'
  else
    render 'edit'
  end
end

当然还有一个视图脚本(在我的例子中是一个 haml 脚本 - app/views/profiles/edit.html.haml)使用 form_for 呈现表单:

= form_for current_user, as: :user, url: profile_path, html: { class: '' } do |f|
# [f.label, f.text_field, f.password_field, etc...]

我的用户模型也包含所有设计优点:

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
  has_many :receipts
  validate :first_name, presence: true
  validate :last_name, presence: true
  validate :email, presence: true, email: true
end

如果你真的想在没有 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