form_for块内的password_field_tag(ruby on rails)

时间:2012-08-24 02:24:25

标签: ruby-on-rails views controllers form-for update-attributes

我的问题是指在更新属性之前通过确认用户的密码来设置视图和控制器以更新用户的“配置文件”。你可能已经看过一百万次了,用户会去/ users /:id / edit,在文本字段中输入一个新的电子邮件,在密码字段中输入当前密码,然后点击提交按钮最终更新用户的电子邮件。如果输入的密码不正确,则再次呈现编辑模板,否则用新电子邮件更新用户记录并重定向到:show(或适用于该应用的任何内容)。在更新操作中,我认为坚持使用update_attributes方法是有意义的。但是,当前的密码值最终会让我们失望。

我真正想问的是我的方法是否有问题。我最终在form_for块中包含对password_field_tag的调用:current_password字段,以便使用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是错误的吗?这些其他网站如何真正做到这一点?我唯一能想到的是他们要么删除:params哈希中的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
      # ...

感谢任何建议。

P.S。 - 此外,您将如何重构更新操作?我尝试使用before_filter进行身份验证:current_password并且只保留#update中的#update_attributes部分,但它有点乱。这篇文章虽然变得足够长,但如果下周我无法解决这个问题,也许我会将其作为一个单独的问题发布。

1 个答案:

答案 0 :(得分:0)

我最近做了类似的事情,除了我使用虚拟属性来处理current_password。然后,您可以将:current_password属性添加到attr_accessible并保持高兴。