mko
mko

Reputation: 22044

Why I can't use t 1:m association in both tables to form a m:m association?

I'm new to rails, I was thinking about this idea this afternoon

here's my code:

video table migration file:

class CreateVideos < ActiveRecord::Migration
  def self.up
    create_table :videos do |t|
      t.string :title
      t.string :drummer_id

      t.timestamps
    end
  end

  def self.down
    drop_table :videos
  end
end

drummer table migration file:

class CreateDrummers < ActiveRecord::Migration
  def self.up
    create_table :drummers do |t|
      t.string :first_name
      t.string :video_id
      t.timestamps
    end
  end

def self.down drop_table :drummers end end

drummer.rb

class Drummer < ActiveRecord::Base
 belongs_to :video
 has_many :videos 
end

video.rb

class Video < ActiveRecord::Base has_many :drummers belongs_to :drummer end

as far as I know

Drummer.create(:first_name => "Jojo")

Drummer.find_by_first_name("Jojo").videos.create(:title => "JojoVideo1")

it's ok

but in the other side, there's nothing!

Video.find_by_title("JojoVideo1").drummers
=>[]

I check the foreign keys all match, But I don't know what the query above return a blank array.

Upvotes: 0

Views: 88

Answers (2)

Iain
Iain

Reputation: 4203

 > Drummer.all
 => [#<Drummer id: 1, first_name: "Jojo", video_id: nil, created_at: "2010-12-10 11:04:48", updated_at: "2010-12-10 11:04:48">] 
 > Video.all
 => [#<Video id: 1, title: "JojoVideo1", drummer_id: "1", created_at: "2010-12-10 11:04:48", updated_at: "2010-12-10 11:04:48">] 
 > Video.all.first.drummer
 => #<Drummer id: 1, first_name: "Jojo", video_id: nil, created_at: "2010-12-10 11:04:48", updated_at: "2010-12-10 11:04:48"> 
 > Video.all.first.drummers
 => [] 

And from test/development.log:

  Video Load (0.3ms)  SELECT "videos".* FROM "videos"
  Drummer Load (0.3ms)  SELECT "drummers".* FROM "drummers" WHERE ("drummers"."id" = 1) LIMIT 1
  Video Load (0.3ms)  SELECT "videos".* FROM "videos"
  Drummer Load (0.3ms)  SELECT "drummers".* FROM "drummers" WHERE ("drummers".video_id = 1)

Video.all.first.drummer works, because it is found by going to the video then checking their drummer id. The other doesn't work because it tries to find drummers with a video_id, and the way we created the video didn't set the drummer's video_id.

We can construct a video/drummer pair with the opposite problem:

d = Drummer.first
d.create_video(:title => "JojoVideo2")
d.save

That new video behaves well for drummers, but poorly for drummer - because this time, the drummer has a video_id but the video has no drummer_id.

The practical upshot of which is that you should do what Rohit said :)

Upvotes: 1

Rohit
Rohit

Reputation: 5721

I don't know what is wrong in your code. But i would like to suggest another way around. You can create a join model which will handle the many to many association for you. Using the join model you can get better control over your code. This can be done as below:

class A < ActiveRecord::Base
has_many :cs
has_many :bs, :through => cs
end

class B < ActiveRecord::Base
has_many :cs
has_many :as, :through => cs
end

class C < ActiveRecord::Base
belongs_to :as
belongs_to :bs
end

and the migration for each model as below

class CreateAs < ActiveRecord::Migration
  def self.up
    create_table :as do |t|
      t.string :title
      t.timestamps
    end
  end

  def self.down
    drop_table :as
  end
end


class CreateBs < ActiveRecord::Migration
  def self.up
    create_table :bs do |t|
      t.string :title
      t.timestamps
    end
  end

  def self.down
    drop_table :bs
  end
end


class CreateCs < ActiveRecord::Migration
  def self.up
    create_table :cs do |t|
      t.references :as
      t.references :bs
      t.timestamps
    end
  end

  def self.down
    drop_table :cs
  end
end

another way around is by just creating a join table without a model. This can be done by:-

class A < ActiveRecord::Base has_and_belongs_to_many :bs end

class B < ActiveRecord::Base has_and_belongs_to_many :as end

Hope you have a better idea.

Upvotes: 1

Related Questions