Why parent model in has_one association has foreign_key = nil - ruby-on-rails

I've two models:
class User < ActiveRecord::Base
before_create :add_address
has_one :address
def add_address
self.address_id ||= Address.generate_new.id
end
end
And
class Address < ActiveRecord::Base
belongs_to :user
def self.generate_new
new_address = # some code generating UUID
Address.create!({address: new_address})
end
end
when I create User.new it creates address association, saves it, and I can fetch it with user.address and in mysql Address row has correct user_id, but User in mysql has address_id = nil. What I'm doing wrong? I've tried User.new\user.build_address both ways it creates and saves address but user always has address_id = nil

This is the way it is supposed to be. From the Rails Doc
The distinction is in where you place the foreign key (it goes on the table for the class declaring the belongs_to association)
This means that since your Address model declares the belongs_to, that is where the foreign key should be placed.
What rails does behind the scenes when you search for user.address is does a search for the Address that has a user_id equal to the current user's id.
Something like:
Select * From addresses where user_id = <current_user>.id

Why do you have address_id column in user model? It should be enough for the association to have user_id on the address table.

Related

Inserting data into foreign key column

I have a user and group table. In user table I have id,name . In group table I have id group_name, created_by , user_id. Here a user can belong to more than one group but not the same group twice. A group can have many users but not the same user twice.
Here is my code.
group.rb
module Client
class Group < ActiveRecord::Base
has_many :users
end
end
user.rb
class User < ActiveRecord::Base
belongs_to :client_groups
end
Now when a user creates a new group, he should be automatically be persisted into the user_id and created_by field.
So as per the guides , I tried to follow the same with my code like this
#user.Client::Group.new(client_group_params)
but it gave me an err.
So I tried another way,
def create
#client_group = Client::Group.new(client_group_params)
client_group_params[:created_by] = current_user.id
client_group_params[:users_id] = current_user.id
#client_group.save
respond_with(#client_group)
end
def client_group_params
params.require(:client_group).permit(:group_name)
end
It saved the group_name into the db but it did not save the created_by and the users_id field. Any idea?
I have made the user_id field foreign key.
First, you need to change the assoication declaration as below:
class User < ActiveRecord::Base
belongs_to :client_group,
class_name: 'Client::Group',
foreign_key: :group_id
end
As per the association you have, the User model should have the column called group_id. Add it through migration. I am not seeing the point of adding the user_id to the Group model.
Then change the create action as :
def create
#client_group = current_user.build_client_group(
client_group_params.merge(created_by: current_user.id)
)
#client_group.save
respond_with(#client_group)
end

ActiveRecord Dual Association

I'm trying to create a model that has a collection, of which there may be 0 or 1 "primary" (or default) item in the collection, I'd like to create an attribute that directly references this primary item.
An example model set might look like:
User < ActiveRecord::Base
belongs_to :company
Company < ActiveRecord::Base
has_many :users
has_one :primary_user, class: 'User'
The "primary" user should be the only result returned by Company.primary_user, but should also be a part of the "users" collection
What are some ways to accomplish this? Reminder, there is only 0 or 1 "primary" allowed, and it must be a member of the general collection.
You could do the following:
In your company table add a field called primary_user_id: integer with a migration (only if you don't have it, I believe you do). In this field your are going to store the id of the primary user for that company. You should set the primary user in the create method of the companies_controller.rb.
In your Company model add the method that returns you the primary user.
Company < ActiveRecord::Base
has_many :users
def primary_user
return User.find(primary_user_id)
end
end
Then whenever you want to use this method you should be careful cause you said there could be no primary_user for some companies. So after calling primary_user method verify if it is not nil.
In any view you got to use this or controller
#bunch of code you got wherever you are
#i want to get the primary user of my first company
company = Company.first
primary_user = company.primary_user
#in order to access field from primary_user, firstly do this
#if primary_user exists
if !primary_user.nil?
#do whatever you want with your primary_user
puts primary_user.name
primary_user.destroy #don't think you want to destroy him
end
You can pass a lambda to has_one and use it to scope the relationship:
has_one :primary_user, -> { where primary: 1 }, class_name: 'User'
This does not enforce that there can be atmost one primary user. That is something you will have to handle elsewhere - eg. through a custom validation.

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.

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.

RoR: How to load the related record in a one-to-one relationship

I have the following:
class User < ActiveRecord::Base
has_one :subscription
end
class Subscription < ActiveRecord::Base
belongs_to :user
end
The User has a subscription_id and thus can have only one subscription (which is what I want).
Which works fine, but now I do:
#users = User.find(:all)
and I want all the subscriptions to be included.
I tried:
#users = User.find(:all, :include=>[:subscription]) # include subscription
But that would like the subscription table to have a user_id (SQLite3::SQLException: no such column: subscriptions.user_id: SELECT "subscriptions".* FROM "subscriptions" WHERE ("subscriptions".user_id = 2)).
Which is (ofcourse) not what I want.
I am new at RoR and I couldn't find a good example of this case in the books I have nor on the web.
I think you have your associations the wrong way round on the model objects. You should have
class User < ActiveRecord::Base
belongs_to :subscription
end
class Subscription < ActiveRecord::Base
has_one :user
end
belongs_to should be used on the side of the association that defines the foreign key (in this case subscription_id). Semantically this probably looks a bit odd, but that's because in this case rails would kind of expect a user_id to be on the subscriptions table instead of the other way round as you have it.
After that
User.find(:all, :include=>[:subscription])
Should work fine
First of all if the user has foreign key (subscription_id) then it should have belongs_to not the other way around. As the Rails docs says for has_one method:
"This method should only be used if the other class contains the foreign key. If the current class contains the foreign key, then you should use belongs_to instead"
(taken from: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#M001834)
Second, in your example you tried to find User and include user. You need to do this:
#users = User.find(:all, :include=>[:subscription])

Resources