当我在Devise Edit表单上验证失败时,将删除配置文件模型

时间:2014-03-16 09:23:45

标签: ruby-on-rails ruby-on-rails-4 devise

所以,我是RoR4的初学者,我以为我会参加一个练习项目。我正在使用Devise作为用户模型,我继续为它添加一个Profile模型:

class User < ActiveRecord::Base
  has_one :profile, dependent: :destroy
  before_create :build_profile # Creates profile at user creation
  accepts_nested_attributes_for :profile
  ...
end

class Profile < ActiveRecord::Base
  belongs_to :user    
  validates :user_id, presence: true      
end

配置/ routes.rb中:

devise_for :users, controllers: { sessions: 'users/sessions', registrations: 'users/registrations' }
resources :users, only: [:index, :show, :destroy]
resources :profiles, only: [:update]

namespace :admin do
  root "base#index"
  resources :users
end

authenticated do
  root to: 'pages#menu', as: :authenticated
end

我基本上希望允许在Devise生成的相同Edit窗体中编辑配置文件。所以我把它添加到application_controller:

def configure_permitted_parameters
...
  devise_parameter_sanitizer.for(:account_update) do |u| u.permit(
      :email, 
      :password, 
      :password_confirmation,
      :current_password,
      :name,
      profile_attributes: [:birthday, :phone, :address, :about, :restrictions, :avatar]
      ) 
  end
end

我按照this page on Devise's wiki上的说明限制仅在电子邮件和密码更改时要求输入密码,如下所示:

  def update
  @user = User.find(current_user.id)

  successfully_updated = if needs_password?(@user, params)
    @user.update_with_password(devise_parameter_sanitizer.sanitize(:account_update))
    else
      # remove the virtual current_password attribute
      # update_without_password doesn't know how to ignore it
      params[:user].delete(:current_password)
      @user.update_without_password(devise_parameter_sanitizer.sanitize(:account_update))
    end

    if successfully_updated
      set_flash_message :notice, :updated
      # Sign in the user bypassing validation in case his password changed
      sign_in @user, :bypass => true
      redirect_to after_update_path_for(@user)
    else
      render "edit"
    end
  end

  private

    # check if we need password to update user data
    # ie if password or email was changed
    # extend this as needed
    def needs_password?(user, params)
      user.email != params[:user][:email] ||
        params[:user][:password].present?
    end

然后我更新了嵌套表单,一切都按预期进行,直到我在更改电子邮件或密码时尝试不提供旧密码。我收到了通知消息,但与Profile相关联的User只是转向了nil,而且它已经消失了。这是服务器日志消息:

Processing by Users::RegistrationsController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"L1i+uV0LFvyVs3xLOMYUJyqeZLnFSWV6R0igyG4+W7E=", "user"=>{"name"=>"Foo Bar1", "profile_attributes"=>{"birthday"=>"", "phone"=>"", "address"=>"", "about"=>"Lorem ipsum dolor", "restrictions"=>"", "avatar"=>"", "id"=>"13"}, "email"=>"user@example.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "current_password"=>"[FILTERED]"}, "commit"=>"Update"}
  User Load (0.3ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 2 ORDER BY "users"."id" ASC LIMIT 1
  User Load (0.3ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
Unpermitted parameters: id
  Profile Load (0.2ms)  SELECT "profiles".* FROM "profiles" WHERE "profiles"."user_id" = ? ORDER BY "profiles"."id" ASC LIMIT 1  [["user_id", 2]]
   (0.1ms)  begin transaction
  SQL (0.4ms)  DELETE FROM "profiles" WHERE "profiles"."id" = ?  [["id", 13]]
   (13.4ms)  commit transaction
  User Exists (0.5ms)  SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'user@example.com' AND "users"."id" != 2) LIMIT 1
  User Exists (0.6ms)  SELECT 1 AS one FROM "users" WHERE (LOWER("users"."username") = LOWER('user1') AND "users"."id" != 2) LIMIT 1

有什么可能出错的建议吗?如何解决?

更新

重新阅读@RichPeck和@KirtiThorat的整个讨论,特别是服务器日志,我设法通过添加配置文件ID作为Devise&中允许的参数之一来解决重新创建配置文件对象和删除旧配置文件的问题。 #39; s :account_update清洁剂,如下:

devise_parameter_sanitizer.for(:account_update) do |u| u.permit(
          :email, 
          :password, 
          :password_confirmation,
          :current_password,
          :name,
          profile_attributes: [:id, :birthday, :phone, :address, :about, :restrictions, :avatar]
          ) 
end

我不知道这对安全性的影响是什么,所以我仍然会感谢您的反馈。

1 个答案:

答案 0 :(得分:0)

首先,如果您设置了Devise以允许用户在不提供密码的情况下编辑其帐户,那么您需要从视图中删除current_password字段以及configure_permitted_parameters方法。

def configure_permitted_parameters
...
  devise_parameter_sanitizer.for(:account_update) do |u| u.permit(
      :email, 
      :password, 
      :password_confirmation,
     ## :current_password,    ## REMOVE THIS LINE
      :name,
      profile_attributes: [:birthday, :phone, :address, :about, :restrictions, :avatar]
      ) 
  end
end

通过指定current_password,您允许account_update进行批量分配。

<强>更新

class User < ActiveRecord::Base
  has_one :profile, dependent: :destroy, inverse_of :user
  before_create :build_profile # Creates profile at user creation
  accepts_nested_attributes_for :profile
  ...
end

class Profile < ActiveRecord::Base
  belongs_to :user, inverse_of :profile    
  validates :user_id, presence: true      
end