James Kerr
James Kerr

Reputation: 326

Rails preview update associations without saving to database

I want to preview what the model will look like when saved without currently saving to the database.

I am using @event.attributes = because that assigns but does not save attributes for @event to the database.

However, when I also try to assign the audiences association, Rails inserts new records into the audiences_events join table. Not cool. Is there a way to preview what these new associations will look like without inserting into the join table?

Model

class Event < ActiveRecord::Base
  has_and_belongs_to_many :audiences # And vice versa for the Audience model.
end

Controller

class EventsController < ApplicationController 

  def preview
    @event = Event.find(params[:id])
    @event.attributes = event_params
  end

  private
    def event_params
      params[:event].permit(:name, :start_time, :audiences => [:id, :name]
    end

end

Possible Solutions?

Possible solutions that I thought of, but don't know how to do:

Any help with these would be great!

UPDATE:
After the reading the great answers below, I ended up writing this service class that assigns the non-nested attributes to the Event model, then calls collection.build on each of the nested params. I made a little gist. Happy to receive comments/suggestions.

https://gist.github.com/jameskerr/69cedb2f30c95342f64a

Upvotes: 7

Views: 5771

Answers (3)

user1914898
user1914898

Reputation: 9

I know that this is a pretty old question, but I found a solution that works perfectly for me and hope it could save time to someone else:

class A
  has_many :bs, class_name 'B'
end

class B
  belongs_to :a, class_name: 'A'
end

a.bs.target.clear
new_bs.each {|new_b| a.bs.build new_b.attributes.except('created_at', 'updated_at', 'id') }

you will avoid autosave that Rails does when you do a.bs = new_bs

Upvotes: 0

max
max

Reputation: 101976

Transactions

Creating transactions is pretty straight forward:

Event.transaction do
  @event.audiences.create!
  @event.audiences.first.destroy!
end

Or

@event.transaction do
  @event.audiences.create!
  @event.audiences.first.destroy!
end

Notice the use of the "bang" methods create! and destroy!, unlike create which returns false create! will raise an exception if it fails and cause the transaction to rollback.

You can also manually trigger a rollback anywhere in the a transaction by raising ActiveRecord::Rollback.

Build

build instantiates a new related object without saving.

event = Event.new(name: 'Party').audiences.build(name: 'Party People')
event.save # saves both event and audiences

Upvotes: 4

george
george

Reputation: 163

In these docs you have:

When are Objects Saved?

When you assign an object to a has_and_belongs_to_many association, that object is automatically saved (in order to update the join table). If you assign multiple objects in one statement, then they are all saved.

If you want to assign an object to a has_and_belongs_to_many association without saving the object, use the collection.build method.

Here is a good answer for Rails 3 that goes over some of the same issues

Rails 3 has_and_belongs_to_many association: how to assign related objects without saving them to the database

Upvotes: 7

Related Questions