问题
I have models
class Agency < ActiveRecord::Base
has_many :specializations
has_many :cruise_lines, through: :specializations
end
class CruiseLine < ActiveRecord::Base
has_many :specializations
has_many :agencies, through: :specializations
end
class Specialization < ActiveRecord::Base
belongs_to :agency, inverse_of: :specializations
belongs_to :cruise_line, inverse_of: :specializations
end
I want to update Specialization
collection (that is delete some of old relations and add a few new if needed). My method that should update relations looks like this (inside some separate service):
def self.update_agency_specializations(agency, params)
attributes = params.require(:agency).permit( { cruise_line_ids: [] } )
attributes[:cruise_line_ids].select{ |x| x.to_i > 0 }.each do |cruise_line_id|
agency.specializations.build(cruise_line_id: cruise_line_id)
end
return false if agency.errors.present?
true
end
But this does basically nothing, however, in combination with updating agency - this exact code worked. What am I doing wrong?
In current implementation it issues error ERROR: duplicate key value violates unique constraint "index_specializations_on_agency_id_and_cruise_line_id" DETAIL: Key (agency_id, cruise_line_id)=(1, 3) already exists.
So that means that it tries to save new relations having not yet deleted old ones, so it violates index of entries that are the same.
回答1:
First solution:
def self.update_agency_specializations(agency, params)
attributes = params.require(:agency).permit( { cruise_line_ids: [] } )
agency.cruise_line_ids = attributes[:cruise_line_ids].select{ |x| x.to_i > 0 }
agency.save
end
!agency.errors.present?
end
In short, don't build anything. Let Rails do the job.
Second solution:
def self.update_agency_specializations(agency, params)
attributes = params.require(:agency).permit( { cruise_line_ids: [] } )
persisted = true
begin
Agency.transaction do
agency.specializations.destroy_all
attributes[:cruise_line_ids].select{ |x| x.to_i > 0 }.each do |cruise_line_id|
agency.specializations.build(cruise_line_id: cruise_line_id)
end
agency.save!
end
rescue RecordInvalid => e
persisted = false
end
persisted
end
It is the extension of your approach and should do the job. Note that in this case all specializations
are destroyed first and then rebuild eventually.
来源:https://stackoverflow.com/questions/26979515/updating-rails-has-many-through-relations