使用强参数和设计更新 OpenStruct 字段
Updating an OpenStruct field with strong parameters and Devise
正在尝试添加 :preferences 以设计强参数
preferences: [:gender, :location, :website, :aim, :interests])
产生错误
Attribute was supposed to be a OpenStruct, but was a ActionController::Parameters. -- {"gender"=>"No", "location"=>"", "website"=>"", "aim"=>"", "interests"=>""}
将 :preferences 添加为
:preferences
没有抛出错误,但是 none 的键值对在提交表单时被更新。 :preferences 以外的字段,如 :email 和 :password,已正确更新。
我已经完成了谷歌搜索,但无法找出在强参数消毒器中指示嵌套 OpenStruct 的格式。
记录器输出确认 :preferences 是不允许的。
controllers/application_controller
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
[snip]
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) {
[snip]
}
devise_parameter_sanitizer.for(:account_update) {
|u| u.permit(:name, :email,
:password, :password_confirmation, :current_password,
preferences: [:gender, :location, :website, :aim, :interests])
}
end
models/user
class User < ActiveRecord::Base
serialize :preferences, OpenStruct
after_create :default_preferences
def default_preferences
self.preferences.gender = ''
self.preferences.location = ''
self.preferences.website = ''
[snip etc.]
end
views/devise/registrations/_form
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
<%= devise_error_messages! %>
<!-- Control panel -->
<% unless params[:page] %>
[snip default control panel page]
<% end %>
<% case params[:page] %>
<% when "password" %>
[snip fields for password change]
<% when "email" %>
[snip fields for email change]
<% when "profile" %>
<%= f.fields_for :preferences, OpenStruct.new(f.object.preferences) do |p| %>
<div class="field">
<%= p.label :gender %>
<div class="input">
<%= p.text_field :gender %>
</div>
</div>
[snip etc. preferences fields, e.g. :location, :website, :aim]
<% end %>
<% end %>
<!-- Submit -->
<div class="field">
<div class="input">
<%= f.submit "Update profile", class: "update-button" %>
</div>
</div>
</div>
<% end %>
您应该能够覆盖首选项分配以按预期类型进行转换:
class User < ActiveRecord::Base
…
def preferences=(val)
return super if val.is_a? OpenStruct
super OpenStruct.new val.to_h if val
end
end
如果值已经是 OpenStruct
,此代码将调用原始设置器方法,否则将尝试将该值转换为散列,然后再将该散列转换为 OpenStruct
。请注意,如果无法将值转换为散列值,这将引发错误。
我认为这是最好的解决方案,因为将 serialize
类型指定为 OpenStruct
意味着分配的值应该 always 是 OpenStruct
,此代码将确保。
正在尝试添加 :preferences 以设计强参数
preferences: [:gender, :location, :website, :aim, :interests])
产生错误
Attribute was supposed to be a OpenStruct, but was a ActionController::Parameters. -- {"gender"=>"No", "location"=>"", "website"=>"", "aim"=>"", "interests"=>""}
将 :preferences 添加为
:preferences
没有抛出错误,但是 none 的键值对在提交表单时被更新。 :preferences 以外的字段,如 :email 和 :password,已正确更新。
我已经完成了谷歌搜索,但无法找出在强参数消毒器中指示嵌套 OpenStruct 的格式。
记录器输出确认 :preferences 是不允许的。
controllers/application_controller
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
[snip]
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) {
[snip]
}
devise_parameter_sanitizer.for(:account_update) {
|u| u.permit(:name, :email,
:password, :password_confirmation, :current_password,
preferences: [:gender, :location, :website, :aim, :interests])
}
end
models/user
class User < ActiveRecord::Base
serialize :preferences, OpenStruct
after_create :default_preferences
def default_preferences
self.preferences.gender = ''
self.preferences.location = ''
self.preferences.website = ''
[snip etc.]
end
views/devise/registrations/_form
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
<%= devise_error_messages! %>
<!-- Control panel -->
<% unless params[:page] %>
[snip default control panel page]
<% end %>
<% case params[:page] %>
<% when "password" %>
[snip fields for password change]
<% when "email" %>
[snip fields for email change]
<% when "profile" %>
<%= f.fields_for :preferences, OpenStruct.new(f.object.preferences) do |p| %>
<div class="field">
<%= p.label :gender %>
<div class="input">
<%= p.text_field :gender %>
</div>
</div>
[snip etc. preferences fields, e.g. :location, :website, :aim]
<% end %>
<% end %>
<!-- Submit -->
<div class="field">
<div class="input">
<%= f.submit "Update profile", class: "update-button" %>
</div>
</div>
</div>
<% end %>
您应该能够覆盖首选项分配以按预期类型进行转换:
class User < ActiveRecord::Base
…
def preferences=(val)
return super if val.is_a? OpenStruct
super OpenStruct.new val.to_h if val
end
end
如果值已经是 OpenStruct
,此代码将调用原始设置器方法,否则将尝试将该值转换为散列,然后再将该散列转换为 OpenStruct
。请注意,如果无法将值转换为散列值,这将引发错误。
我认为这是最好的解决方案,因为将 serialize
类型指定为 OpenStruct
意味着分配的值应该 always 是 OpenStruct
,此代码将确保。