4

I am trying to create a form to save quite a complexish relationship. The models for the relationship are below.

class Goal < ActiveRecord::Base
  belongs_to :goal_status
  belongs_to :goal_type

  has_many :users, through: :user_goals
  has_many :user_goals

  has_many :parent_goals, class_name: 'GoalDependency', foreign_key: :parent_id
  has_many :child_goals, class_name: 'GoalDependency', foreign_key: :child_id

  has_many :children, through: :child_goals
  has_many :parents, through: :parent_goals

  validates_presence_of :goal_status_id, :goal_type_id
end

class GoalDependency < ActiveRecord::Base
  belongs_to :parent, class_name: 'Goal', foreign_key: 'parent_id'
  belongs_to :child, class_name: 'Goal', foreign_key: 'child_id'
end

So a goal can have many parents or it can have many children or both. I have tried using a multi select drop down to save these relationships and setting child_ids/parent_ids, but that doesn't seem to work because the goal_dependency requires both fields - i.e. child_id and parent_id. Rails only sets the one. So if I am saving a list of child_ids it sets those, but it doesn't know to set the parent_id with the current goal and visa versa.

I have tried using accepts_nested_attributes, but I am not really sure how I can use this with a multi-select drop down.

Any guidance or direction on how I could approach this would be appreciated.

Example of my current form.

.row
  .col-md-6
    = simple_form_for(@goal) do |f|
      - if @goal.errors.any?
        #error_explanation
          h2 = "#{pluralize(@goal.errors.count, 'error')} prohibited this goal from being saved:"

          ul
            -@goal.errors.full_messages.each do |message|
              li = message

      = f.input :description
      = f.input :goal_status_id, collection: @goal_statuses, value_method: :id, label_method: :name, as: :select
      = f.input :goal_type_id, collection: @goal_types, value_method: :id, label_method: :name, as: :select
      = f.input :user_ids, collection: @users, as: :select, label: 'Assigned To', input_html: { class: 'chosen-select', multiple: true }, selected: @goal.user_ids
      = f.input :child_ids, collection: @goals, as: :select, label: 'Children Goals', input_html: { class: 'chosen-select', multiple: true }, selected: @goal.child_ids, value_method: :id, label_method: :description

      br
      = f.submit

After further investigation I thought of another way to do this, but I am not really happy with it. I can split the concept of a dependency into a parent dependency and a child dependency and have these as different models. The relationship can then be handled differently. See the code below for the child dependency.

class Goal < ActiveRecord::Base
  belongs_to :goal_status
  belongs_to :goal_type

  has_many :users, through: :user_goals
  has_many :user_goals

  has_many :child_goals, class_name: 'GoalChildDependency'
  has_many :children, through: :child_goals

  validates_presence_of :goal_status_id, :goal_type_id
end

class GoalChildDependency < ActiveRecord::Base
  belongs_to :goal
  belongs_to :child, class_name: 'Goal', foreign_key: :child_id
end

From what I have read rails can't deal with composite keys and my case seems to be a case where a composite key would make sense.

Anyway if anyone can figure out how to make my initial code work that would be great.

RAJ
  • 9,697
  • 1
  • 33
  • 63
Ryan-Neal Mes
  • 6,003
  • 7
  • 52
  • 77
  • You need to post your current form code at the very least. – Beartech Mar 16 '15 at 19:47
  • Have you tried using association fields like this? `= f.association(:children, collection: Goal.all, include_blank: false, input_html: { class: 'chosen-select' })` – Mikhail Janowski Mar 17 '15 at 07:15
  • No that doesn't work. I am getting the same params back from the form. `"goal"=>{"description"=>"Deliver System", "goal_status_id"=>"2", "goal_type_id"=>"1", "user_ids"=>["", "1", "2", "4"], "child_ids"=>["", "1", "2"]}, "commit"=>"Update Goal", "id"=>"1"` - I think when it saves the child_ids it doesn't know that is has to set the parent_id to the goal_id I am editing. – Ryan-Neal Mes Mar 17 '15 at 08:43

1 Answers1

0

Maybe it would be just easier to have single Goal model with self referencing parent_id column on it. Where than you can make

has_many :child_goals, class_name: 'Goal', foreign_key: :parent_id

belongs_to :parent_goal, class_name: 'Goal'

Additionally if you need ChildGoal model anyway, you can have a single table inheritance models, add column type to goals table and have ChildGoal < Goal and follow analogous approach as above.

ClassyPimp
  • 715
  • 7
  • 20