我的问题是指通过在更新属性之前确认用户的密码来设置视图和控制器以更新用户的“配置文件”。正如您之前可能已经看过一百万次一样,用户会转到 /users/:id/edit,在文本字段中输入新电子邮件,在密码字段中输入当前密码,然后单击提交按钮最终更新用户的电子邮件。如果输入的密码不正确,则再次呈现编辑模板,否则将使用新电子邮件更新用户记录并重定向到 :show (或适用于应用程序的任何内容)。在更新操作中,我认为坚持使用 update_attributes 方法是有意义的。然而,当前的密码值最终会让我们失望。
我真正要问的是我的方法是否有问题。我最终在 form_for 块中为 :current_password 字段调用 password_field_tag ,以便使用 params[:user] 调用 update_attributes 而不会使 attr_accessible 生气。但是后来我在已经这样做的网站中查找了几个表单(例如 hulu 和 destroyallsoftware),它们似乎接受了用户哈希中的 :current_password 值(假设它们是用 rails 构建的)。查看 twitter 的设置页面,看起来他们在 param 中的单独哈希中检索到这个(所以 params[:current_password] 而不是 params[:user][:current_password])。
在 form_for 中使用 password_field_tag 有错吗?这些其他网站是如何真正做到这一点的?我唯一能想到的是他们要么从参数哈希中删除 :current_password 要么单独分配每个属性。
这是我基本上结束的:
# /app/models/user.rb
class User < Activerecord::Base
attr_accessible :email, # ...
# ...
end
# /app/views/users/edit.html.erb
<%= form_for @user do |f| %>
# this is stored in params[:user][:email]
<%= f.label :email, 'Your new email' %>
<%= f.text_field :email, type: :email %>
# this is stored in params[:current_password]
<%= label_tag :current_password, 'Re-enter your password to update your email' %>
<%= password_field_tag :current_password %>
<%= f.submit 'Save changes' %>
<% end %>
# /app/controllers/users_controller.rb
# ...
def update
@user = User.find(params[:id])
if @user.authenticate(params[:current_password])
if @user.update_attributes(params[:user])
sign_in @user
flash[:success] = 'Sweet!'
redirect_to @user
else
render :edit
end
else
flash.now[:error] = 'Incorrect password'
render :edit
end
否则,这是我想到的另一种方式:
# /app/views/users/edit.html.erb
<%= form_for @user do |f| %>
# this is stored in params[:user][:email]
<%= f.label :email, 'Your new email' %>
<%= f.text_field :email, type: :email %>
# this is stored in params[:user][:current_password]
<%= f.label :current_password, 'Re-enter your password to update your email' %>
<%= f.password_field :current_password %>
<%= f.submit 'Save changes' %>
<% end %>
# /app/controllers/users_controller.rb
# ...
def update
@user = User.find(params[:id])
if @user.authenticate(params[:user][:current_password])
params[:user].delete(:current_password) # <-- this makes me feel a bit uneasy
if @user.update_attributes(params[:user])
sign_in @user
flash[:success] = 'Sweet!'
redirect_to @user
else
render :edit
end
else
flash.now[:error] = 'Incorrect password'
render :edit
end
或者,我应该在控制器中执行此操作吗?:
def update
@user = User.find(params[:id])
if @user.authenticate(params[:user][:current_password])
@user.email = params[:user][:email]
if @user.save
# ...
任何建议表示赞赏。
PS - 此外,您将如何重构该更新操作?我尝试了一个 before_filter 来使用 :current_password 进行身份验证,并在 #update 中只保留 #update_attributes 部分,但它有点乱。这篇文章已经足够长了,所以如果我下周无法弄清楚,也许我会把它作为一个单独的问题发布。