Ruby: has_one relationship with foreign key? - ruby-on-rails

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.

Related

How to scope Rails model id which is dependent on belongs_to association?

I have next structure of models in my app:
class Company
has_many :employees
end
class Employee
belongs_to :company
end
Is there a way to make it possible for employees to have unique ids (default primary keys) depending on belongs_to Company association?
These should return different Employee models:
/companies/1/employees/1
/companies/2/employees/1
Thanks!
Try the sequenced gem, it does exactly what you're asking. There is one consideration to keep in mind though.
Your requirement deprives Employee's id field of uniqueness which it needs to be a primary key. Therefore you'd either need to have a composite key in Employee, namely [:company_id, :employee_id] or use the Employee's acts_as_sequenced field not as the primary key but more like a slug.
Just in case you care to explore the composite key approach, there is composite_primary_key gem which aims to support ActiveRecord associations on top of composite keys. I haven't tried it myself though.
According to its docs, your associations could look something like this:
class Company < ActiveRecord::Base
has_many :employees, :foreign_key => [:company_id, :employee_id]
end
class Employee < ActiveRecord::Base
self.primary_keys = :user_id, :employee_id
belongs_to :company
end
But its quite likely this is an overkill approach for your goal.

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.

Rails many-to-many relations and nested forms: associate with object if it already exists

I'm having an issue with several many-to-many relations in my Rails project. It can be illustrated with an example:
Say I have the models Person and PhoneNumber, joined by PersonPhoneNumber. The relation is many-to-many because people can have more than one phone number, and more than one person can be reached at the same phone number (in a case such as a help desk).
class Person < ActiveRecord::Base
has_many :person_phone_numbers
has_many :phone_numbers, :through => :person_phone_numbers
end
class PhoneNumber < ActiveRecord::Base
has_many :person_phone_numbers
has_many :people, :through => :person_phone_numbers
validates :number, :uniqueness => true
end
class PersonPhoneNumber < ActiveRecord::Base
belongs_to :person
belongs_to :phone_number
end
I have a person form that lets me create/update people's contact information. I use it to assign the number 555-555-1212 to Bob. If a PhoneNumber object with that number doesn't exist, I want it to be created (as in the standard accepts_nested_attributes_for behavior). But if it does exist, I want to just create a PersonPhoneNumber object to associate Bob with that PhoneNumber.
How can I accomplish this most elegantly? I tried putting a before_validation hook in PersonPhoneNumber to look for a matching PhoneNumber and set phone_number_id, but this caused really bizarre behavior (including making my Rails server crash with the message Illegal instruction: 4).
You can use exists? method to check for existence first, like this:
#person.phone_numbers.build(number: "555-555-1212") unless #person.phone_numbers.exists(number: "555-555-1212")
Or you can do something like this:
PhoneNumber.find_or_create(person_id: #person.id, number: "555-555-1212")
Rachel the Rails documentation says this:
A has_and_belongs_to_many association creates a direct many-to-many connection with another model, with no intervening model.
What is the difference?

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.

What's the difference between belongs_to and has_one?

What is the difference between a belongs_to and a has_one?
Reading the Ruby on Rails guide hasn't helped me.
They essentially do the same thing, the only difference is what side of the relationship you are on. If a User has a Profile, then in the User class you'd have has_one :profile and in the Profile class you'd have belongs_to :user. To determine who "has" the other object, look at where the foreign key is. We can say that a User "has" a Profile because the profiles table has a user_id column. If there was a column called profile_id on the users table, however, we would say that a Profile has a User, and the belongs_to/has_one locations would be swapped.
here is a more detailed explanation.
It's about where the foreign key sits.
class Foo < AR:Base
end
If foo belongs_to :bar, then the foos table has a bar_id column
If foo has_one :bar, then the bars table has a foo_id column
On the conceptual level, if your class A has a has_one relationship with class B then class A is the parent of class B hence your class B will have a belongs_to relationship with class A since it is the child of class A.
Both express a 1-1 relationship. The difference is mostly where to place the foreign key, which goes on the table for the class declaring the belongs_to relationship.
class User < ActiveRecord::Base
# I reference an account.
belongs_to :account
end
class Account < ActiveRecord::Base
# One user references me.
has_one :user
end
The tables for these classes could look something like:
CREATE TABLE users (
id int(11) NOT NULL auto_increment,
account_id int(11) default NULL,
name varchar default NULL,
PRIMARY KEY (id)
)
CREATE TABLE accounts (
id int(11) NOT NULL auto_increment,
name varchar default NULL,
PRIMARY KEY (id)
)
has_one and belongs_to generally are same in a sense that they point to the other related model. belongs_to make sure that this model has the foreign_key defined.
has_one makes sure that the other model has_foreign key defined.
To be more specific, there are two sides of relationship, one is the Owner and other is Belongings. If only has_one is defined we can get its Belongings but cannot get the Owner from the belongings. To trace the Owner we need to define the belongs_to as well in the belonging model.
One additional thing that I want to add is, suppose we have the following models association.
class Author < ApplicationRecord
has_many :books
end
If we only write the above association, then we can get all books of a particular author with
#books = #author.books
but, for a particular book, we can't get the corresponding author with
#author = #book.author
To make the above code work we need to add an association to the Book model as well, like this
class Book < ApplicationRecord
belongs_to :author
end
This will add method 'author' to the Book model. For mode details see guides
has_one
This method should only be used if the other class contains the foreign key.
belongs_to
This method should only be used if the current class contains the foreign key.
From a simplicity standpoint, belongs_to is better than has_one because in has_one, you would have to add the following constraints to the model and table that has the foreign key to enforce the has_one relationship:
validates :foreign_key, presence: true, uniqueness: true
add a database unique index on the foreign key.

Resources