Rails 7中has_many through编辑记录时无法加载关联数据
问题:编辑记录时关联的services集合为空,但查询语句正确
我有以下模型:
class Property < ApplicationRecord belongs_to :property_type belongs_to :landlord belongs_to :property_transaction has_many_attached :photos has_many :included_amenities, dependent: :destroy has_many :included_services, dependent: :destroy has_many :amenities, through: :included_amenities has_many :services, through: :included_services end class Amenity < ApplicationRecord has_many :included_amenities has_many :properties, through: :included_amenities validates_presence_of :name, :en_translation_key validates_uniqueness_of :name, :en_translation_key end class Service < ApplicationRecord has_many :included_services has_many :properties, through: :included_services validates_presence_of :name, :en_translation_key validates_uniqueness_of :name, :en_translation_key end class IncludedAmenity < ApplicationRecord belongs_to :amenity belongs_to :property end class IncludedService < ApplicationRecord belongs_to :service belongs_to :property end
通过UI创建新记录时,关联模型能成功保存到中间表,但编辑记录时,控制器中调用property.services得到空集合,奇怪的是ActiveRecord执行了正确的INNER JOIN查询:
Processing by PropertiesController#edit as HTML Parameters: {"id"=>"2"} Property Load (0.4ms) SELECT "properties".* FROM "properties" WHERE "properties"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]] ↳ app/controllers/properties_controller.rb:158:in `set_property' Service Load (0.3ms) SELECT "services".* FROM "services" INNER JOIN "included_services" ON "services"."id" = "included_services"."service_id" WHERE "included_services"."property_id" = $1 [["property_id", 2]] ↳ app/controllers/properties_controller.rb:73:in `edit' CACHE Service Load (0.0ms) SELECT "services".* FROM "services" INNER JOIN "included_services" ON "services"."id" = "included_services"."service_id" WHERE "included_services"."property_id" = $1 [["property_id", 2]] ↳ app/controllers/properties_controller.rb:73:in `edit'
查询语句是正确的,我遗漏了什么?附上_form.html.erb视图模板:
<div class="form-group mb-3"> <%= form.label :services, 'Services', class: 'form-label' %> <%= form.collection_check_boxes(:services, @services, :id, :name) do |c| %> <div class="custom-control custom-checkbox"> <%= c.check_box(class: 'custom-control-input') %> <%= c.label(class: 'custom-control-label') %> </div> <% end %> </div>
补充控制器代码,我原以为只要表单关联@property及其相关属性就会自动绑定,是不是哪里错了?
class PropertiesController < ApplicationController before_action :set_property, only: %i[show edit update destroy] before_action :set_proptypes, only: %i[index edit update new create] before_action :set_services, only: %i[edit update new create] # ... # GET /properties/1/edit def edit puts @property end def set_property @property = Property.find(params[:id]) end def set_services @services = Service.select(:id, :name) end end
解决方案
问题出在强参数配置和表单参数名称两个点上:
表单参数名称错误:
Rails处理has_many through关联时,需要使用xxx_ids格式的参数来批量维护关联关系,而不是直接用关联名称services。当前视图生成的参数是property[services][],无法被正确识别。强参数未允许关联字段:
控制器的update方法中,大概率没有允许service_ids参数,导致关联数据无法被正确处理。
具体修复步骤:
- 修改视图中的
collection_check_boxes参数为:service_ids:
<%= form.collection_check_boxes(:service_ids, @services, :id, :name) do |c| %>
- 在控制器的
property_params方法中添加service_ids: []到允许的参数列表:
def property_params params.require(:property).permit( # 保留你已有的其他参数... service_ids: [] ) end
- 可选优化:为避免N+1查询并确保关联数据正确加载,在
set_property中预加载services:
def set_property @property = Property.includes(:services).find(params[:id]) end
完成以上修改后,编辑页面会正确预选中已关联的服务,提交表单时关联关系会被正确更新,同时@property.services也会返回预期的非空集合。
内容的提问来源于stack exchange,提问作者Manuel Santiago Yépez
相关产品推荐
相关产品推荐

