自定义设计字段在更新时不保存(在创建时有效)Rails 5,设计 4,Ruby 2.3

Custom devise field not saving on update (works on create) Rails 5, Devise 4, Ruby 2.3

使用 Rails 5,Ruby 2.3,设计 4

您好,我一直在尝试构建一个简单的 rails 应用程序,其中包含首选用户和非首选用户。我希望用户能够从更新帐户页面在首选和非首选之间切换,并且似乎无法在提交时将更改保存到 current_user。

preferred 是我的用户 table 中的一个布尔值,我的表单可以在创建新用户时将该值设置为 true 或 false。

这是我的 users/registrations/edit.html.erb

的副本

<h2>Edit <%= resource_name.to_s.humanize %></h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
  <%= devise_error_messages! %>

  <div class="field">
    <%= f.label :email %><br />
    <%= f.email_field :email, autofocus: true %>
  </div>

  <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
    <div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
  <% end %>

  <div class="field">
    <%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
    <%= f.password_field :password, autocomplete: "off" %>
    <% if @minimum_password_length %>
      <br />
      <em><%= @minimum_password_length %> characters minimum</em>
    <% end %>
  </div>

  <div class="field">
    <%= f.label :password_confirmation %><br />
    <%= f.password_field :password_confirmation, autocomplete: "off" %>
  </div>

  <div class="field">
    <%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
    <%= f.password_field :current_password, autocomplete: "off" %>
  </div>
  
  <div class="field">
    <%= f.label :preferred, "Preferred" %><br />
    <% if current_user.preferred? %>
      <%= f.check_box :preferred, :checked => true, :value => true %>
    <% elsif !current_user.preferred? %>
      <%= f.check_box :preferred, :checked => false, :value => false %>
    <% end %>
  </div>

  <div class="actions">
    <%= f.submit "Update" %>
  </div>
<% end %>

<h3>Cancel my account</h3>

<p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p>

<%= link_to "Back", :back %>

以及更新测试

require "rails_helper"

RSpec.feature "Account Updated" do
  # create a user that whose account will be updated
  before do
    @user = User.create(email: "johndoe@example.com", password: "password1@", password_confirmation: "password1@", preferred: "false")  
  end
  
  scenario "A user opts into preferred" do
    # visit root
    visit "/"
    # user signs in
    click_link "Log in / Sign up"
    expect(page.current_path).to eq(new_user_session_path)
    fill_in "Email", with: @user.email
    fill_in "Password", with: @user.password
    click_button "Log in"
    expect(page.current_path).to eq(root_path)
    # user goes to account page
    click_link("My Account")
    expect(page.current_path).to eq(edit_user_registration_path)
    # preferred checkbox should be unchecked
    expect(page.has_unchecked_field?("Preferred"))
    # user checks preferred status
    check("Preferred")
    # user saves changes
    click_button "Update"
    # expect user to be preferred
    expect(@user.preferred).to eq(true)
    # user recieves success message
    expect(page).to have_content("You updated your account successfully.")
  end
end

以上测试失败,结果如下:

1) Account Updated A user opts into preferred
     Failure/Error: expect(@user.preferred).to eq(true)
       
       expected: true
            got: false
       
       (compared using ==)
     # ./spec/features/account_updates_spec.rb:29:in `block (2 levels) in <top (required)>'

Finished in 0.52815 seconds (files took 2.84 seconds to load)

这是我的应用程序控制器

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  before_action :configure_permitted_parameters, if: :devise_controller?
  
  protected
  
  # allow custom sanitizer(s)
  def devise_parameter_sanitizer
    if resource_class == User
      UserParameterSanitizer.new(User, :user, params)
    else
      super # use default
    end
  end
  
  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:account_update, keys: [:preferred, :email, :password, :password_confirmation])
  end
  
end

以及用于让自定义字段节省新用户的用户参数消毒器

class UserParameterSanitizer < Devise::ParameterSanitizer
  def initilaize(*)
    super
    permit(:sign_up, keys: [:preferred])
  end
end
  

最后是我的用户模型

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
end

我是 rails 的新手,可能会忽略一些简单的事情,但我已经坚持了几个小时,但我尝试过的任何东西似乎都不起作用。

我现在已经开始工作了。我摆脱了 UserParameterSanitzer,并像这样在我的应用程序控制器中完成了所有工作

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  before_action :configure_permitted_parameters, if: :devise_controller?
  
  protected
  
  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up) do |user_params|
      user_params.permit(:email, :password, :password_confirmation, :preferred)
    end
    devise_parameter_sanitizer.permit(:account_update) do |user_params|
      user_params.permit(:email, :password, :password_confirmation, :preferred, :current_password)
    end
  end
  
end

我也加了

fill_in "Current password", with: @user.password

根据我的测试,由于设计需要当前密码才能保存,所以没有保存。