Rails创建Booking时无法获取嵌套profile参数更新用户资料
问题描述
现有3个Rails ActiveRecord模型,需求为完成Booking创建操作后,若用户提交的资料有变更,同步更新对应用户的Profile信息。当前提交表单后booking_params仅返回:poi_id、:owner_id、:user_id三个参数,无法获取Profile相关字段,无法完成Profile更新。
现有模型定义
user.rb
class User < ApplicationRecord has_one :profile, inverse_of: :user, dependent: :destroy accepts_nested_attributes_for :profile has_many :bookings, inverse_of: :user, dependent: :destroy has_many :owner_bookings, foreign_key: 'owner_id', class_name: 'Booking' end
profile.rb
class Profile < ApplicationRecord # mount_uploader :avatar, AvatarUploader belongs_to :user, inverse_of: :profile accepts_nested_attributes_for :user end
booking.rb
class Booking < ApplicationRecord belongs_to :user belongs_to :owner, class_name: :User belongs_to :poi end
原有问题代码
表单代码
<%= simple_form_for [@poi.poitable, @booking] do |f| %> <%= f.simple_fields_for @user.profile do |profile| %> <%= profile.input :first_name, input_html: { value: @user.profile.first_name } %> <%= profile.input :last_name, input_html: { value: @user.profile.last_name } %> <% end %> <%= f.input :owner_id, input_html: { value: @poi.user.id } %> <%= f.input :poi_id, input_html: { value: @poi.id } %> <%= f.input :user_id, input_html: { value: @user.id } %> <%= f.submit %> <% end %>
控制器代码
def new @user = current_user @poi = Sleep.friendly.find(params[:sleep_id]).poi @booking = Booking.new end def create @user = current_user @poi = Sleep.friendly.find(params[:sleep_id]).poi @booking = Booking.new(booking_params) if @booking.save @booking.user.profile.update_attribute(booking_params[:user_profile_attributes]) end private def booking_params params.require(:booking).permit( :poi_id, :owner_id, :user_id, user_profile_attributes: [:user_id, :first_name, :first_name], ) end
修复方案
问题由三处写法错误导致,按以下步骤修改即可:
- 修正表单嵌套字段写法
直接给simple_fields_for传@user.profile对象时,生成的表单字段name属性会自动生成为booking[profile][xxx],和控制器里许可的user_profile_attributes键名不匹配,同时不需要手动给input加value属性,表单构建器会自动绑定值。另外需要加hidden类型的id字段,否则更新时无法定位到现有Profile记录。
将表单内嵌套字段部分替换为:
<%= f.simple_fields_for :user_profile_attributes, @user.profile do |profile| %> <%= profile.input :first_name %> <%= profile.input :last_name %> <%= profile.input :id, as: :hidden %> <% end %>
- 修正控制器参数许可与更新逻辑
首先删除参数许可里重复声明的:first_name,补充Profile的:id字段;创建Booking实例时需要剔除user_profile_attributes参数,避免Booking模型无对应属性报批量赋值错误;update_attribute是单字段更新方法,批量更新字段需要用update方法。
修改后的控制器相关代码如下:
def create @user = current_user @poi = Sleep.friendly.find(params[:sleep_id]).poi @booking = Booking.new(booking_params.except(:user_profile_attributes)) if @booking.save @booking.user.profile.update(booking_params[:user_profile_attributes]) # 补充保存成功后的跳转逻辑 else # 补充校验失败的渲染逻辑 end end private def booking_params params.require(:booking).permit( :poi_id, :owner_id, :user_id, user_profile_attributes: [:id, :first_name, :last_name] ) end
修改完成后重启服务,即可正常拿到Profile相关参数完成更新。
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

