Rails - left shift "<<" automatically saves the record

I need help understanding this code, as far as I know I know "<<add to the collection, but here it correctly saves the record, how does this happen without calling the .save method?

 #user.rb has_many :saved_properties, through: :property_saves, source: :property #users_controller.rb def update if @user.saved_properties << Property.find(params[:saved_property_id]) render plain: "Property saved" end 
+6
source share
3 answers

Perhaps a source code search will help you. This is my search trail based on the << method in activerecord :

 def <<(*records) proxy_association.concat(records) && self end 

rails / collection_proxy.rb at 5053d5251fb8c03e666f1f8b765464ec33e3066eRails / RailsGitHub

 def concat(*records) records = records.flatten if owner.new_record? load_target concat_records(records) else transaction { concat_records(records) } end end 

rails / collection_association.rb at 5053d5251fb8c03e666f1f8b765464ec33e3066eRails / RailsGitHub

 def concat_records(records, should_raise = false) result = true records.each do |record| raise_on_type_mismatch!(record) add_to_target(record) do |rec| result &&= insert_record(rec, true, should_raise) unless owner.new_record? end end result && records end 

rails / collection_association.rb at 5053d5251fb8c03e666f1f8b765464ec33e3066eRails / RailsGitHub

  def insert_record(record, validate = true, raise = false) set_owner_attributes(record) set_inverse_instance(record) if raise record.save!(validate: validate) else record.save(validate: validate) end end 

https://github.com/rails/rails/blob/5053d5251fb8c03e666f1f8b765464ec33e3066e/activerecord/lib/active_record/associations/has_many_association.rb#L32

  def insert_record(record, validate = true, raise = false) ensure_not_nested if record.new_record? || record.has_changes_to_save? if raise record.save!(validate: validate) else return unless record.save(validate: validate) end end save_through_record(record) record end 

https://github.com/rails/rails/blob/5053d5251fb8c03e666f1f8b765464ec33e3066e/activerecord/lib/active_record/associations/has_many_through_association.rb#L38

As you can see, in the end it calls the save method.

Disclaimer: I am not familiar with Rails code, but you have an interesting question.

+6
source

The has_many documentation says:

Adds one or more objects to the collection by setting their keys to the primary key of the collection. Note that this operation instantly starts SQL update without waiting for the save or update to invoke the parent if the parent is not a new record.

+6
source

For has_many , the link information is stored in the target record. This means that << will have to modify this entry to add it to the set.

Perhaps with the intention of convenience, ActiveRecord automatically saves them for you when completing the task, if the task was successful. The exception is for new records, the record with which they are associated does not have an identifier, so it must be deferred. They are saved when the record with which they are associated is finally created.

This may be a bit confusing, perhaps unexpected, but in fact this is what you would like to do 99% of the time. If you do not want this to happen, you must manually manipulate the link:

  property = Property.find(params[:saved_property_id]) property.user = @user property.save! 

This is basically equivalent, but much more verbose.

+2
source

Source: https://habr.com/ru/post/1013681/


All Articles