我想在设计时更新没有密码的用户属性。情况是这样的,如果密码和密码确认字段不为空,那么我需要设计错误,如果它们为空,则需要更新其他用户属性。我怎样才能通过设计做到这一点?
提前致谢!
Answers:
我认为这是一个更好的解决方案:
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
操作从表单中设置新参数时使用它即可。
current_password
。它设计用于除更改密码字段外还显示所有用户属性(名称,电子邮件等)的情况。没有这样的逻辑,如果用户提交的表单更改了其他字段,而密码字段为空,则Devise将不允许更新任何属性。
我认为使用Devise 3.2+,您可以在子类控制器中重写update_resource。这是最初询问的问题的示例:
class MyRegistrationsController < Devise::RegistrationsController
protected
def update_resource(resource, params)
resource.update_without_password(params)
end
end
devise_for :users, controllers: {registrations: 'registrations'}
attr_accessor :current_password
到我的用户模型中。我不喜欢这种骇客。我是不是该?
params.delete :current_password
之前添加resource.update_without_password(params)
我解决了以下问题:如果表单使用密码提交,则需要填写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
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
我用我的界面解决了这个问题。它可以有两种方法。
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();
});
无论哪种情况,都不需要更新应用程序本身。您只是在提交要更改的字段。
我改写了#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
如果您仍然希望支持密码更改但将其设置为可选,请检查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,则可以继续并更新密码,否则将其忽略并不使用密码进行更新。
如果您希望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中。
这是更多的业务逻辑,因此我不会在控制器中放入太多代码。我建议Devise::Models::Validatable#password_required?
您在模型中进行覆盖:
def password_required?
new_record? || password.present? || password_confirmation.present?
end
对于未来的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复杂的处理方式而发生变化。
不客气
在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/
我遇到了完全相同的问题,这是我想出的解决方案,并且相信我可以使用。我所做的是创建第二种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
经过对上述可能性的大量探索,我终于找到了一种解决方案,该解决方案使您无需密码即可更新某些属性,而有些则需要:
我用以下格式查看了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)
我承认这是一个很大的工作,但是没有一个简单的解决方案可以解决所有上述问题。
更好,更短的方法:将检查参数添加到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
要更新用户的属性,您将需要使用: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
第一行检查“您的用户是否发送了正确的密码”,然后我们就可以更新属性而不会产生垃圾
Devise::RegistrationsController
s中的方法。可以查看以前版本的Wiki历史记录,但可以考虑他的建议。