Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails - Override primary key on has_one

I have the following associations, basically I want to link via userid and not the id of the object.

class Tweet < ActiveRecord::Base
  has_one :user_profile, :primary_key => 'userid', :foreign_key => 'twitter_userid' 

class UserProfile < ActiveRecord::Base
  belongs_to :tweet, :foreign_key => 'userid'

However the following spec fails as twitter_userid is reset to the id of the object

it "should have the user's twitter id set on their user profile" do
   t = Tweet.new(:twitter_id => 1,
                  :status => 'Tester', 
                  :userid => 'personA',
                  :user_profile => UserProfile.new(:twitter_userid => 'personA', :avatar => 'abc'))
   t.save!
    t.user_profile.twitter_userid.should == 'personA'
 end

should have the user's twitter id set on their user profile

expected: "personA",
    got: 216 (using ==)

However, the following does pass:

 it "should return the correct avatar after being saved" do
   t = Tweet.new(:twitter_id => 1,
                  :status => 'Tester', 
                  :userid => 'personA', 
                  :user_profile => UserProfile.new(:twitter_userid => 'personA', :avatar => 'abc'))
   t.save!

   t.user_profile.avatar.should == 'abc'
 end

How can I force it to use userid and not id?

Thanks

Ben

like image 997
Ben Hall Avatar asked Sep 10 '09 17:09

Ben Hall


2 Answers

There are two things going on here. I think you have switched the :has_one and :belongs_to declarations.

The :belongs_to comes in the model that has the foreign-key. The :has_one comes in the model that is referred to (if it is only one, otherwise if many rows :belong_to the same, it is a has_many of course). For more info read here.

First, you will need to specify (overrule) the primary key of the model. Here i am assuming a user can have many tweets instead of just one (otherwise replace by :has_one).

class UserProfile
  set_primary_key :userid
  has_many :tweets 
end

then you need to adjust your :has_one declaration:

class Tweet
  belongs_to :user_profile, :foreign_key => :userid
end

Then, secondly, once your relations are setup correctly, there is no need to assign both :user_profile or :userid at creation time. Either you create a new UserProfile and the foreign-keys will automatically by correct, or you assign an userid of an existing profile.

Hope this helps.

like image 154
nathanvda Avatar answered Oct 11 '22 02:10

nathanvda


cite@antiope:/tmp/foo$ script/console
Loading development environment (Rails 2.3.4)
>> t = Tweet.new(:twitter_id => 1,
?>                   :status => 'Tester', 
?>                   :userid => 'personA',
?>                   :user_profile => UserProfile.new(:twitter_userid => 'personA', :avatar => 'abc'))
=> #<Tweet id: nil, twitter_id: 1, status: "Tester", userid: "personA", created_at: nil, updated_at: nil>
>> Tweet.set_primary_key :userid
=> nil
>> t.save
  Tweet Create (0.4ms)   INSERT INTO "tweets" ("created_at", "updated_at", "userid", "twitter_id", "status") VALUES('2009-09-10 20:19:36', '2009-09-10 20:19:36', 'personA', 1, 'Tester')
  UserProfile Create (0.1ms)   INSERT INTO "user_profiles" ("twitter_userid", "created_at", "updated_at", "avatar") VALUES('personA', '2009-09-10 20:19:36', '2009-09-10 20:19:36', 'abc')
=> true
>> Tweet.set_primary_key :id
=> nil

Modfiying the model a split second before saving it might be an acceptable solution if you only have to redefine the primary key in one place (I didn't test if modifying the Tweet class like that affected only the current controller or all actions). Still, it's only what I consider to be a workaround.

like image 27
cite Avatar answered Oct 11 '22 04:10

cite