Rails foreign key other than id - ruby-on-rails

I have an ActiveRecord named Ad, and it has column id and server_id. They are both unique. id is given by Rails, but what's meaningful here is server_id.
Then I need to create another ActiveRecord named Bid which has 1 to 1 relationship with Ad. Bid has a key ad_id which refers to Ad.server_id.
I know that I should specify Ad has_one Bid and Bid belongs_to Ad, and specify foreign key name through foreign_key: "ad_id". What's troubling me here is I can only find out how to let Bid.ad_id refer to Ad.id while I want it refers to Ad.server_id.
Can anybody show me how to achieve this? Thanks!

User primary key
Bid.rb
belongs_to :ad, :foreign_key => :ad_id, :primary_key => :server_id
Ad.rb
has_one :bid, :foreign_key => :ad_id, :primary_key => :server_id
foregin_key option is not needed here but adding to make the difference between it and primary_key clear.

Have you tried using 'references' when generating your model.
i.e.
rails generate model Advertisement bid:references

Related

Rails belongs_to returns nil class

I am trying to link two tables to each other
class Musers < ActiveRecord::Base
# Email
# sid (student_id:integer)
# isyk: boolean
belongs_to :user, :foreign_key => "smail"
end
class Users < ActiveRecord::Base
belongs_to :muser, :foreign_key => "email"
end
But,
#user = Users.first
#user.muser returns nil
By saying :foreign_key => "smail" you are telling rails that the Muser column smail points to the User model's foreign key. This is most likely not the case.
Assuming that the primary key of the User models is called id, you should a user_id field to Muser, and change belongs_to :user, :foreign_key => "smail" into:
belongs_to :user
On the User model you can define the reverse relation using:
has_one :muser
Also, to follow rails model naming conventions, you should rename Users to User and Musers to Muser.
You should read more about belongs_to and has_one.
If, on the other hand, the User model in fact uses email for it's primary key, I would strongly advise you to change that and add an auto-incrementing primary key instead. As a rule of thumb, the primary key should be chosen such that it never changes. If it does change, all foreign keys pointing to that primary key will have to change as well.
You should only use a non auto-incrementing primary key if you have a specific reason for doing so.
More information on choosing a primary key: How to choose my primary key?
Well you can't just tell rails the type of association, you actually have to set the association to an instance of that class. For example, making a new muser will not automatically assign a user as the belongs_to. You could do something like
u = User.new
u.muser = Muser.first
u.save
However, I'm not sure what you are trying to accomplish with a belongs_to - belongs_to relationship, but you should know that you have to do more than just tell rails it exists.

How to specify polymorphic association with custom primary keys in Rails

I have a few ActiveRecord classes using paper_trail for version tracking. The AR classes have custom primary keys based on their table names (e.g. Item.ItemID instead of Item.id) in order to adhere to business DB conventions.
paper_trail specifies a polymorphic relationship on each of the tracked classes, thus:
class TrackedExample < ActiveRecord::Base
set_table_name 'TrackedExample'
set_primary_key 'TrackedExampleID'
# simplified from https://github.com/airblade/paper_trail/blob/master/lib/paper_trail/has_paper_trail.rb
has_many :versions
:class_name => 'Version'
:as => :item,
end
class AnotherTrackedExample
set_table_name 'AnotherTrackedExample'
set_primary_key 'AnotherTrackedExampleID'
has_many :versions
:class_name => 'Version'
:as => :item,
end
# from https://github.com/airblade/paper_trail/blob/master/lib/paper_trail/version.rb
class Version
belongs_to :item, :polymorphic => true
...
end
If I were not using custom primary keys, the version object could refer to the tracked object (i.e. the object of which it is a version) using Version#item. When I try it, I get an error:
# This should give back `my_tracked_example`
my_tracked_example.version.first.item
=> TinyTds::Error: Invalid column name 'id'.: EXEC sp_executesql N'SELECT TOP (1) [TrackedExample].* FROM [TrackedExample] WHERE [TrackedExample].[id] = 1 ORDER BY TrackedExample.TrackedExampleID ASC'
Is there a way to get Version#item to perform the correct query? I would expect something like this:
EXEC sp_executesql N'SELECT TOP (1) [TrackedExample].* FROM [TrackedExample] WHERE [TrackedExample].[TrackedExampleID] = 1 ORDER BY TrackedExample.TrackedExampleID ASC'
I'm using Rails 3.1.0, paper_trail 2.6.4 and MS SQL Server through TinyTDS and activerecord-sqlserver-adapter.
EDIT: I've worked around the problem by adding computed columns TrackedExample.id and AnotherTrackedExample.id that refer to the primary key values. This isn't a proper solution (Rails is still making the wrong query), but it may be useful to others in a hurry.
MS SQL:
ALTER TABLE TrackedExample
ADD COLUMN id AS TrackedExampleID
Rather than specifying t.references :item, polymorphic: true in your migration file.
You simply have to specify the item_id and item_type as follows:
t.string :item_id
t.string :item_type
Rails will automatically select the correct primary_key for the item_id and correct type.
I haven't tried it, but this might work
class Version
belongs_to :item, :polymorphic => true, :primary_key => 'TrackedExampleID'
...
end
After scouring the PaperTrail documents it doesn't look like you can override what column item_id references (i.e. the primary key of the Item's table), so I think you have two main options:
1. Create an id column that does not have the name of the class in it. So, id instead of TrackedExampleID.
You said you already did this as a quick fix.
2. Fork and patch PaperTrail to allow you to pass what column to use when querying for item_id.
This could either be the value set with set_primary_key 'TrackedExampleID' or it could be something set like has_paper_trail primary_key: 'TrakedExampleID'.
Let us know what you end up with.

ActiveRecord foreign key set to null

I am using Ruby 1.8.7 and Rails 2.3.8 and I have the following root resource:
class PointOfInterest < ActiveRecord::Base
set_primary_key "Id"
set_table_name "POI"
has_many :attributes, :foreign_key => 'POIId'
end
The point of interest can have several attributes:
class Attribute < ActiveRecord::Base
set_primary_key "Id"
set_table_name "Attribute"
belongs_to :point_of_interest, :foreign_key => 'POIId'
has_one :multimedia, :foreign_key => 'Id', :primary_key => 'Value'
end
The attribute class may have media associated with it:
class Multimedia < ActiveRecord::Base
set_primary_key "Id"
set_table_name "Multimedia"
end
I am trying to insert a point of interest in my database like so:
poi = PointOfInterest.new
attr = poi.attributes.new
attr.SomeAttribute = 1
attr.build_multimedia(:content => 'test')
poi.save
This is properly persisting both the root (PointOfInterest) and the Multimedia record. The Attribute, however, is not being properly persisted. While the foreign key to the point of interest is properly set (POIId), the foreign key to the Multimedia record remains null.
Any clue as to why this is very much appreciated!
Thanks!
Your relationship / foreign key are set at cross purposes.
If Attribute has_one Multimedia, then you need the Multimedia model to declare belongs_to :attribute, and the Multimedia table to contain a foreign key for Attribute.
I'm guessing that you can't change your database schema (otherwise, I'd have to ask why you're using non-Rails-standard table and key names); in which case you want to switch the sense of the relation. Make Attribute belongs_to :multimedia, and Multimedia has_one :attribute. Then the Multimedia FK in the Attribute table is pointing in the right direction.

conflict between attribute name and foreign key name

I am wondering if it is possible to have the following situation: I have a person object that has both an attribute called email and a foreign key called email which is realized as a has_many relationship. The latter is supposed to contain not only the primary email but also secondary ones. Would this cause conflict when I invoke #person.email? What would be the standard way to work around this? Thanks.
Create a secound field that could be used as a foreign key and then in the associated model add :foreign_key => 'your_foreign_key' as aparameter to the existing association like this:
has_many :email_addresses, :class_name => 'ClassName', :foreign_key => 'your_foreign_key'
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html <-- Documentation about that.

Ruby: has_one relationship with foreign key?

I have a model called "EmployeeRecord" and it has a field called username, There is another model called "Employee" with a field called username.
I want to create an association between the 2 models so when I do:
record = EmployeeRecord.find(1)
record.employee // returns Employee instance
I was thinking I'd just need to do this, but apparently it doesn't work:
class EmployeeRecord < ActiveRecord::Base
has_one :employee, :foreign_key: username
end
Assume I can't add an employee_id field to EmployeeRecord. I scoured the Rails tutorials.. and recall wanting to know how to do this months ago.. but those dang Rails tutorials glided over this.. I remember.. it made me very very angry hehe
Any idea?
You'll also need to specify the primary_key used for the association, or else it's defaulted to "id". Your statement actually says "Search for a field username in table employee that is equal to my id field". What you actually want is "Search for a field username in table employee that is equal to my username field"
This should do the trick :
class EmployeeRecord < ActiveRecord::Base
has_one :employee, :foreign_key => username, :primary_key => :username
end
But hey... Why don't you use ids?
Add the foreign key option in the belongs_to method in your Employee model.
class Employee < ActiveRecord::Base
belongs_to :employee_record, foreign_key: username
end
The has_one or has_many is the parent, so it doesn't store the foreign key value/column. That's what the child does that has the belong_to side of the relationship.

Resources