Question

I have a form which creates a new Child record and a new Parent record using accepts_nested_attributes_for

Children and Parents have a has_many :through association like so:

class Child < ActiveRecord::Base
   has_many :relationships
   has_many :parents, through: :relationships
   accepts_nested_attributes_for :parents
   accepts_nested_attributes_for :relationships, allow_destroy: true
end

class Parent < ActiveRecord::Base
   has_many :relationships
   has_many :children, through: :relationships
end

class Relationship < ActiveRecord::Base
   belongs_to :child
   belongs_to :parent
   accepts_nested_attributes_for :parents

   #has :schedule attribute
end

I would like to set the schedule attribute in Relationships at the same time and can't use accepts_nested_attributes_for as I am manipulating some data in the controller to determine the schedule object before saving.

There are many answers on stack overflow for accomplishing this with a pre-existing parent record but I am having trouble finding any for a situation where none of the associations exist previously.

I have tried to do this with an after_create callback in the model but don't have access to params and have read that it is bad procedure to do so anyway. ( Rails How to pass params from controller to after_save inside model).

Ideally I would like to set schedule in the same database call that creates the relationship record along with child_id and parent_id.

Any help with this would be awesome, thanks

UPDATE

Controller as of original post

def new
  @child = Child.new
end

def create
  @child = Child.new(params[:child])
  schedule = build_schedule(params)
  # Need to save schedule to @child.relationship[0].schedule somehow
  if @child.save!
    redirect_to admins_path
  else
    render 'new'
  end
end

private

def build_schedule(params)
  # works with params[:mon, :tue, :wed, :thu, :fri] to return a schedule object.
end

The parent is built using Ryan Bates' nested form helper. This is the build line from it:

module ApplicationHelper
  def link_to_add_fields(name, f, association)
    new_object = f.object.send(association).klass.new
    ...
  end
end

I am not building the relationship at all, it is magically built by rails as far as i'm aware.

Trying Danny Van Hoof's suggestion @child.relationships[0].schedule = i get undefined method schedule=' for nil:NilClass as no relationship exists.

If i build the relationship beforehand either in new or create action using @child.relationship.build I get 2 relationship records being saved:

relationships table

The one that I built has schedule saved but has no parent_id (parental_group_id) The second record is the one build magically and has both correct ids but no schedule.

Now when trying Rick Peck's suggestion by adding

def params
    params.require(:child).permit(relationships_attributes: [:schedule])
    # tried with and without `parents_attributes`
end 

I get a stack level too deep error on the params.require line. I also don't fully understand what this does. (I'm fairly new to rails)

Thanks for helping!

Was it helpful?

Solution

I have a similar construct as yours in one of my websites, it works fine, except that I (translated to your example) have

attr_accessible :relationships_attributes
accepts_nested_attributes_for :relationships, allow_destroy: true

In my Relationship model, I then make parent_id accessible

This allows to, in the controller, to do something like

@child.relationships[0].schedule = ...

Even before saving the child

Or, alternatively, to set the schedule from your view immediately when entering the parent details

Just drop me a comment if not clear!

OTHER TIPS

Firstly, your accepts_nested_attributes_for will have to include the join model, and then pass the data through to the other model, like this:

class Child < ActiveRecord::Base
   has_many :relationships
   has_many :parents, through: :relationships

   accepts_nested_attributes_for :relationships
end

class Parent < ActiveRecord::Base
   has_many :relationships
   has_many :children, through: :relationships
end

class Relationship < ActiveRecord::Base
   belongs_to :child
   belongs_to :parent

   #attr_accessor :schedule ?
   accepts_nested_attributes_for :parents
end

#app/controllers/child_controller.rb
def new
    @child = Child.new
    @child.relationships.build.parents.build
end

private
def params
    params.require(:child).permit(relationships_attributes: [:schedule, parents_attributes: [:variable-1, :variable-2]])
end

This will pass the required data to relationships and then onto parents. The schedule can be then handled when you handle the variables in the relationships model

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top