okysabeni okysabeni - 1 year ago 72
Ruby Question

Rails self referential has_many through: is not adding the right record

Been wrecking my brain all day for this. I created a has_many through relationships for a

so each match can have many other matches. I created a table called

create_table :pool_tournament_match_relationships do |t|
t.belongs_to :parent, class_name: 'PoolTournamentMatch', index: true


has_many :pool_tournament_match_relationships,
class_name: 'PoolTournamentMatchRelationship',
foreign_key: :parent_id
has_many :parents, through: :pool_tournament_match_relationships

So, I should be able to do something like
match.pool_tournament_match_relationships.create(parent: anotherMatch)

However, when I do this, the record added to the relationships table is actually that of
and not
. So, for example if match id is 1 and anotherMatch id is 2. 1 will be entered into the relationships table.

Here is the output from the console:

m.pool_tournament_match_relationships.create(parent: m2)

INSERT INTO "pool_tournament_match_relationships" ("parent_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["parent_id", 1], ["created_at", 2016-11-08 21:51:29 UTC], ["updated_at", 2016-11-08 21:51:29 UTC]]

Notice that
entered is 1 which is the id of m instead of m2.

irb(main):012:0> m.id
=> 1
irb(main):013:0> m2.id
=> 5

I appreciate your help!

EDIT: Add the schema for the relationships table:

create_table "pool_tournament_match_relationships", force: :cascade do |t|
t.integer "parent_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["parent_id"], name: "index_pool_tournament_match_relationships_on_parent_id", using: :btree

Answer Source

With has-many-through relationships, you shouldn't have to mess with creating the records for the relationship table or assigning attributes to them. Just use basic operators on the operative classes and let Rails figure out the relationships for you.

The names of your classes are long and I'm not quite straight in my head how they work, so I'll illustrate with some from one of my projects.

# app/models/event.rb

class Event < ActiveRecord::Base
  has_many :aid_stations, dependent: :destroy
  has_many :splits, through: :aid_stations

# app/models/split.rb

class Split < ActiveRecord::Base
  has_many :aid_stations, dependent: :destroy
  has_many :events, through: :aid_stations

# app/models/aid_station.rb

class AidStation < ActiveRecord::Base
  belongs_to :event
  belongs_to :split

# app/controllers/events_controller.rb

class EventsController < ApplicationController
  before_action :set_event

  def associate_split
    @event.splits << Split.find(params[:split_id])
    redirect_to splits_event_path(id: @event.id)

  def remove_split
    redirect_to splits_event_path(@event)


  def set_event
    @event = Event.find(params[:id])

Note that associate_split creates an aid_station record and remove_split destroys one, but there is no mention of any aid_stations in the methods. Rails takes care of that behind the scenes.

This would work equally well in the splits_controller if I had chosen to put the associate and remove methods there.

EDIT: Here's the relevant portion of the schema for aid_stations:

  create_table "aid_stations", force: :cascade do |t|
    t.integer  "event_id"
    t.integer  "split_id"
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download