has_many, through, foreign key problems

Becca Girl wrote:

Article has_many :readings has_many :users, :through => :readings

Readings belongs_to :article, :foreign_key => :article_id belongs_to :user, :foreign_key => :user_id

Users has_many :readings has_many :articles, through => readings

Add the foreign keys to the has_many side, too:

Article has_many :readings, :foreign_key => :article_id

etc

Becca Girl wrote:

But my sql query has not changed. Maybe I need to change the code that generates the query?

@articles = Articles.find(:all, :conditions => "user_id = 1", :include => :readings)

Sorry, I wasn't thinking right. The foreign key specifications on the has_many/belongs_to association refers to the name of the column on the belongs_to side only. Since this column is named correctly anyway you don't need to specify this on either side.

The actual issue here is that you are performing the Rails equivalent of a foreign key lookup and a foreign key must connect to a primary key.

In Rails, when handling legacy tables, the primary key of a table is not always the one you want Rails to see as the primary key. In your example, the Article model (presumably using an articles table?) has a database column of id which is the table's primary key, but from the Rails perspective, the article_id column is the primary key. Use:

class Article self.primary_key = :article_id has_many :readings has_many :users, :through => :readings

class Reading belongs_to :article belongs_to :user

If the same situation occurs in the User class, then do the same thing with that association.

Mark Bush wrote:

class Article self.primary_key = :article_id has_many :readings has_many :users, :through => :readings

class Reading belongs_to :article belongs_to :user

Thanks Mark. Here's a funny thing though, the primary key of article is not article_id, it's actually id. The article_id is actually how these two tables connect to each other. So the primary key needs to be used in other areas of the site, but not in this situation. Again, legacy data is making it a bit more difficult.

Any way to tell this to not use id and instead use article_id in this instance?

This is probably out of my depth, but I'll blunder in anyway. Could maybe a view rescue you here? If all you need to do is rename some fields, maybe you could create a view & point rails at that?

Just a thought...

Becca Girl wrote:

Any way to tell this to not use id and instead use article_id in this instance?

The primary key is a class attribute. You could try adding interfaces to these queries in your Article model where the primary key is temporarily changed. Something like:

class Article   # association stuff

  def self.article_id_find *args     self.primary_key = :article_id     find *args   ensure     self.primary_key = :id   end

If this is running under mongrel it should be ok as mongrel is single threaded, but it's a bit of a kludge.

Mark Bush wrote:

class Article   # association stuff

  def self.article_id_find *args     self.primary_key = :article_id     find *args   ensure     self.primary_key = :id   end

If this is running under mongrel it should be ok as mongrel is single threaded, but it's a bit of a kludge.

I dropped in your code (using my field names), but the query stays the same. Ahead of this method, I have self.primary_key defined since due to legacy data it's table_id, not id.

table structure: table_id (primary key) article_id (links to readings)

So my code now looks like this

self.primary_key = "table_id"

def self.pre_mover_legacy_id_find *args   self.primary_key = :article_id   find *args ensure   self.primary_key = "table_id" end

The sql query still tries to join readings.article_id to article.table_id instead of article.article_id.

@articles = Articles.find(:all, :conditions => "user_id = 1", :include => :readings)

Any guidance on what @articles should look like now or is there something else I should change somewhere else?

Mark Bush wrote:

@articles = Article. pre_mover_legacy_id_find :all,                                     :include => :users,                                     :conditions => ["users.id = ?", 1]

You've been so great! Hopefully you have a little more time.

Here are all of my parts and pieces that are throwing an error.

Models:

class Article has_many :readings has_many :users, :through => :readings self.primary_key = "table_id"

def self.article_id_find *args   self.primary_key = :article_id   find *args ensure   self.primary_key = "table_id" end end # class Article

class Reading belongs_to :article belongs_to :user end # class Reading

class User has_many :readings has_many :articles, :through => :readings end # class User

UsersController

@articles = Article.article_id_find :all, :include => :users, :conditions => ["users.id = ?", 1]

Error: uninitialized constant UsersController::Articles

Becca Girl wrote:

Error: uninitialized constant UsersController::Articles

This indicates that you are referring to "Articles" (plural) instead of "Article" in the UsersController somewhere and Rails can't work out what it should be...

To be sure the query is working, try the #article_id_find method in the console...

You need to specify the primary key for the article table.

This is REALLY not recommended.

Julian