【发布时间】:2011-07-04 01:12:53
【问题描述】:
我想在设计中更新没有密码的用户属性。情况就像,如果密码和密码确认字段不为空,那么我需要设计错误,如果它们为空,则需要更新其他用户属性。我怎么能用设计做到这一点?
提前致谢!
【问题讨论】:
标签: ruby-on-rails devise
我想在设计中更新没有密码的用户属性。情况就像,如果密码和密码确认字段不为空,那么我需要设计错误,如果它们为空,则需要更新其他用户属性。我怎么能用设计做到这一点?
提前致谢!
【问题讨论】:
标签: ruby-on-rails devise
如果你真的想在没有 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
【讨论】:
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/
【讨论】:
对于未来的 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 复杂的做事方式简单、简单、容易,而且不会乱七八糟。
不客气。
【讨论】:
要更新用户的属性,您需要使用 :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
第一行检查'你的用户是否发送了正确的密码',然后我们可以更新属性没有垃圾
【讨论】:
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
【讨论】:
更好更短的方法:将检查参数添加到 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
【讨论】:
在对上述可能性进行了多次探索之后,我终于找到了一个解决方案,它允许您在没有密码的情况下更新一些属性,而另一些则可以:
我为 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)
我承认这是一项巨大的工作,但没有一个更简单的解决方案可以解决上述所有问题。
【讨论】:
我发现上面代码的细微变化更容易理解:
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
【讨论】:
如果您仍想支持密码更改但将其设为可选,请检查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 存在,您可以继续并更新密码,否则忽略它并在没有密码的情况下更新。
【讨论】:
作为用户模型中的解决方法
def password_required?
encrypted_password.blank? || encrypted_password_changed?
end
【讨论】:
我遇到了完全相同的问题,这是我想出的解决方案,相信我它有效。
我所做的是创建第二个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
【讨论】:
更多的是业务逻辑,所以我不会在控制器中放置太多代码。我建议在您的模型中覆盖 Devise::Models::Validatable#password_required?:
def password_required?
new_record? || password.present? || password_confirmation.present?
end
【讨论】:
我改为覆盖#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
【讨论】:
如果您希望 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中。
【讨论】:
我用我的界面解决了这个问题。它可以有两种方式。
这部分 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();
});
在任何一种情况下,都不需要更新应用程序本身。您只是提交要更改的字段。
【讨论】:
这就是你要找的吗?来自设计维基
Allow users to edit their account without providing a password
它展示了如何为 rails 3 和 rails 4 执行此操作
=========================
John's solution 是一个更简单的替代方案
【讨论】:
Devise::RegistrationsControllers 中的方法。可以查看wiki history 了解以前的版本,但请考虑他的建议。
我认为使用 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 添加到我的用户模型中。我不喜欢的那种黑客。我应该吗?
resource.update_without_password(params)之前简单地添加params.delete :current_password
我认为这是一个更好的解决方案:
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 操作中使用的任何内容来设置表单中的新参数。
【讨论】:
current_password。它专为显示所有用户属性(姓名、电子邮件等)的情况而设计,除了字段用于更改密码。如果没有这样的逻辑,如果用户提交的表单更改了其他字段而密码字段为空,则 Devise 将不允许更新任何属性。
我解决了这个问题如下:如果表单使用密码提交,则需要填写 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
【讨论】: