how to avoid overwriting nested attributes? - ruby-on-rails

I have:
class Activity < ActiveRecord::Base
has_and_belongs_to_many :balance_sheets
end
and
class BalanceSheet < ActiveRecord::Base
has_and_belongs_to_many :activities
accepts_nested_attributes_for :activities
end
When I perform an UPDATE sending "balance_sheet"=>{"activity_ids"=>["10", "20"]} if I have previous activities loaded on the balance_sheet object, the activities collection is replaced. I don't want override the activities old values, I want to add new ones. How can I do this?

Don't update the BalanceSheet object directly. Instead, create an intermediate BalanceSheetUpdater class which will take your activity_ids and append it to the existing balance_sheet's activity_ids.
class BalanceSheetUpdater
def initialize balance_sheet
#balance_sheet = balance_sheet
end
def call(balance_sheet_params)
new_ids = balance_sheet_params.delete("activity_ids")
update_status = #balance_sheet.update(balance_sheet_params)
if update_status
existing_ids = #balance_sheet.activity_ids
#balance_sheet.update(existing_ids + new_ids)
end
update_status
end
end
# controller
updater = BalanceSheetUpdater.new(#balance_sheet)
if updater.call(balance_sheet_params)
... # success / fail actions

Related

How can I create all has_one relationships automatically?

I have the following models:
class Post < ApplicationRecord
has_one: :basic_metric
has_one: :complex_metric
end
class BasicMetric < ApplicationRecord
belongs_to :post
end
class ComplexMetric < ApplicationRecord
belongs_to :post
end
Once a post is created, both basic_metric and complex_metric are nil:
p = Post.first
p.basic_metric # => nil
p.complex_metric # => nil
And because of how my app is going to work, the BasicMetricsController and ComplexMetricsController only have the update method. So I would like to know if there is a way to create them as soon as a post is created.
One very common way of accomplishing this is using ActiveRecord callbacks
class Post
after_create :create_metrics
private
def create_metrics
# methods created by has_one, suggested in the comments
create_basic_metric(additional_attrs)
create_complex_metric(additional_attrs)
end
end
Another option you have is to overwrite the method created by has_one, i.e.:
class Post
has_one: :basic_metric
has_one: :complex_metric
def basic_metric
super || create_basic_metric(additional_attrs)
end
def complex_metric
super || create_complex_metric(additional_attrs)
end
end
This way they won't be created automatically with any new post, but created on demand when the method is called.
Can you try this one,
post = Post.first
post.build_basic_metric
post.build_complex_metric
This will help you to build/create the has_one association object if record not saved by default use post.save at the end.
If you need this in modal you can use like this,
class Post
after_create :build_association_object
private
def create_metrics
self.build_basic_metric
self.build_complex_metric
# save # (optional)
end
end

Saving two records in Rails at the same time where one has a foreign_key pointing to the other

class Pool
has_many :memberships
end
class Membership
belongs_to :pool, presence: true
end
This is the association I have. Whenever a new pool is created, I also want to create an entry into the membership class.
class Api::PoolsController < ApplicationController
def create
#pool = Pool.new(pool_params)
member = Membership.new(user_id: current_user.id, pool_id: #pool.id)
...
end
end
The issue is that #pool is not currently saved to the database yet, so it does not have an id value.
Is there a way that I can tell member to add the id after #pool is saved? Basically linking them to each other?
class Api::PoolsController < ApplicationController
def create
#pool = Pool.new(pool_params)
member = #pool.memberships.new(user_id: current_user.id)
...
end
end

how can we get only newly added object after update using accepts_nested_attributes_for assocation in rails

My association looks like this :
class Abc < ApplicationRecord
has_many :def
accepts_nested_attributes_for :def, allow_destroy: true
end
class AbcController < ApplicationController
def update
abc = Abc.find(params[:id])
if abc.update(abc_params)
# TODO Here update is sucessful but how to get all newly added def in database with their id?
end
end
private
def abc_params
params.require(:abc).permit(def_attributes: [:name, :title,:wordcount, :id])
end
end
We know accepts_nested attributes creates a new nested object in the database so how can I get all the newly added(not already existing) def object with their database id in AbcController update function ?
One solution is (not a direct one)..
def update
abc = Abc.find(params[:id])
number_of_defs = abc.defs.length
if abc.update(abc_params)
number_newly_added_defs = abc.defs.length - number_of_defs
newly_added_defs = abc.defs.last(number_newly_added_defs)
end
end
you will get the desired output.

Rails create callback for 2 dependent resources

I want to know how i can use create callback like before_create or after_create for the two dependent models.
class User < ActiveRecord::Base
end
class Member < ActiveRecord::Base
end
Lets suppose i have two models called User and Member and i want to create a member whenever any user will be created and want to create user whenever any member will be created .
If i will use the after_create or before_create callback in both the models it will run as never ending loop .so how this can be done.
Just check if either of the association exists in db before creating it in after_create callback, something like this:
class User < ActiveRecord::Base
after_create :create_member
private
def create_member
unless self.member?
# create member
end
end
end
class Member < ActiveRecord::Base
after_create :create_user
private
def create_user
unless self.user?
# create user
end
end
end

Rails, using build to create a nested object, but it is not being saved

My app has three models:
Thread (has_many :thread_apps)
ThreadApp (belongs_to :thread, has_many :forms, :as => :appable)
Form (belongs_to :app)
ThreadApp Fields: thread_id, form_id, appable_id, appable_type
What I want to be able to do is when creating a form, ensure that a ThreadApp record is also created to make the association:
Here is what I have:
class FormsController < ApplicationController
def create
#thread = Thread.find(params[:thread_id])
#thread_app = #thread.thread_apps.new
#form = #thread_app.forms.build(params[:form].merge(:user_id => current_user.id))
#form.save
....
This is saving the form nicely, but the thread_app associated is not being made? Any ideas why?
Thank you
callings model.save does not save associations unless you tell it to
you can set autosave
class Form < ActiveRecord::Base
belongs_to :thread_app , :autosave => true
end
or call save on it in the controller
#thread_app.save
or you can take it out of the controller entirely
and do this with a callback
class Form < ActiveRecord::Base
before_create :create_thread_app
def create_thread_app
self.thread_app ||= ThreadApp.create(...)
end
end
or after_create, _before_validation_on_create, or any other call back would work
--UPDATE--
this might make a difference using create inse=tead of new, and appables that you specified as 'as'
class FormsController < ApplicationController
def create
#thread = Thread.find(params[:thread_id])
#thread_app = #thread.thread_apps.create
#form = #thread_app.appables.build(params[:form].merge(:user_id => current_user.id))
#form.save
....
Instead of:
#thread_app = #thread.thread_apps.new
You should have:
#thread_app = #thread.thread_apps.create

Resources