I have the following models:
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
I can confirm that when I create a new record via UI, the associated models get saved successfully into the intermediate table. However, when I try to edit the record via UI, I can also see that Rails loads an empty hash if I call property.services
at controller level, for example. What's also weird is that supposedly ActiveRecord is indeed calling the correct INNER JOINS:
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'
The query is correct as well. What am I missing? Just in case, here's the template for _form.html.erb view:
<div >
<%= form.label :services, 'Services', class: 'form-label' %>
<%= form.collection_check_boxes(:services, @services, :id, :name) do |c| %>
<div >
<%= c.check_box(class: 'custom-control-input') %>
<%= c.label(class: 'custom-control-label') %>
</div>
<% end %>
</div>
Edit: Adding controller code. As far as I had understood, Rails matches whenever the form has a @property and its related attributes and ties it to the form, or am I missing something?
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
CodePudding user response:
Not sure why I needed to do this, I'm guessing it's a workaround:
<div >
<%= form.label :services, 'Services', class: 'form-label' %>
<%= form.collection_check_boxes(:services, @services, :id, :name) do |c| %>
<div >
<%= c.check_box(checked: property.included_services.map(&:service_id).include?(c.value), class: 'custom-control-input') %>
<%= c.label(class: 'custom-control-label') %>
</div>
<% end %>
</div>