Foreign Key class_name - ruby-on-rails

So, this is my first time using foreign keys, and though I think they are working properly, I don't understand the class_name portion in the syntax. Is that the class_name that the table being referred to is in?
My code:
Game Model:
belongs_to :user, foreign_key: 'white_player_id', class_name: 'User'
belongs_to :user, foreign_key: 'black_player_id', class_name: 'User'
User Model:
has_many :games, foreign_key: 'white_player_id', class_name: 'Game'
has_many :games, foreign_key: 'black_player_id', class_name: 'Game'
I was looking at: http://ricostacruz.com/cheatsheets/rails-models.html and noitced that in their example they have the class name of both belongs_to and has_many pointing to the Folder class..
belongs_to :parent, :foreign_key => 'parent_id' class_name: 'Folder'
has_many :folders, :foreign_key => 'parent_id', class_name: 'Folder'
So, that leads me to believe that the class name is supposed to point to the class that contains the foreign_key?? A little insight would be much appreciated.

If you define multiple assocations with the same name your just overwriting the same assocation.
belongs_to :white_player, foreign_key: 'white_player_id', class_name: 'User'
belongs_to :black_player, foreign_key: 'black_player_id', class_name: 'User'
class_name is the class of the related object.
foreign_key refers to the table of the model class where you are defining the relationship when defining a belongs_to relationsship.
class Game < ActiveRecord::Base
belongs_to :white_player, foreign_key: 'white_player_id', class_name: 'User'
# foreign_key is game.white_player_id
end
So when we do game.white_player Active Record looks for:
User.find(game.white_player_id)
added:
In your second example foreign_key in has_many refers to the related table.
belongs_to :parent, :foreign_key => 'parent_id' class_name: 'Folder'
has_many :folders, :foreign_key => 'parent_id', class_name: 'Folder'
And you would not need to specify the foreign key and class name explicitly:
class Folder < ActiveRecord::Base
# ActiveRecord will infer that the class name is Folder
has_many :folders, foreign_key: 'parent_id'
# Rails will infer that the foreign_key is parent_id
belongs_to :parent, class_name: 'Folder'
end
As you can see ActiveRecord is one smart cookie and can infer class names and foreign keys.
Here is an easier to explain example of has_many and foreign_keys:
class User < ActiveRecord::Base
has_many :unread_messages, -> { where read: false },
foreign_key: 'recipient_id', # refers to messages.recipient_id
class_name: 'Message'
end
user.unread_messages will query the table message:
SELECT "messages".* FROM "messages" WHERE "messages"."recipient_id" # ...

class_name is for the class which is used in method you use, if ActiveRecord can not decide which class it is. In your example you dont need class_name because your method is user and class that will connect to is User, ActiveRecord can figure that out on its own.
You have another problem. You have two relations with same name user and user, that is not posible.
You could made it like this though:
Game Model:
belongs_to :white_player, foreign_key: 'white_player_id', class_name: 'User'
belongs_to :black_player, foreign_key: 'black_player_id', class_name: 'User'
UserModel:
has_many :white_games, class_name: 'Game'
has_many :black_games, class_name: 'Game'

You might want to create it with a new model, the simpliest way to do it :
Migration - 1/2 : console
rails g model Game white_player:references black_player:references
Migration - 2/2 : db/migrate/create_games.rb
In the migration file, delete the "foreign_key: true" entry, that would look like :
t.references :white_player, foreign_key: true
t.references :black_player, foreign_key: true
Run : rails db:migrate
Model files :
Game Model :
belongs_to :white_player, class_name: 'User'
belongs_to :black_player, class_name: 'User'
User Model :
has_many :white_player_games, class_name: 'Game', foreign_key: 'white_player_id'
has_many :black_player_games, class_name: 'Game', foreign_key: 'white_player_id'
Hope that helps.

Related

rails model with multiple related addresses

I have the following rails models:
# Address Model
address_line_1
address_line_2
address_line_3
post_code
country
# Vendor model
name
registered_address_id
billing_address_id
display_address_id
registered_address, billing_address and display_address should refer to an address in the address table.
so eventually I can do Vendor.registered_address etc.
I am confused about how to go about this with regards to migrations and my relationships on my model, can anyone point me in the right direction?
Many thanks
You can do it declaring explicitly class_name on belongs_to and has_many and foreign_key on has_many
class Vendor < ApplicationModel
belongs_to :registered_address, class_name: 'Address'
belongs_to :billing_address, class_name: 'Address'
belongs_to :display_address, class_name: 'Address'
end
class Address < ApplicationModel
has_many :registered_addresses, foreign_key: :registered_address_id, class_name: 'Vendor'
has_many :billing_addresses, foreign_key: :billing_address_id, class_name: 'Vendor'
has_many :display_addresses, foreign_key: :billing_address_id, class_name: 'Vendor'
end
References: -
has_many
belongs_to
Query =>
vendor_object.registered_address
vendor_object.billing_address
vendor_object.display_address

Rails one model two associations

I have a User model and a Task model. All users are the same and each user can create a new task and assign that task to another user. In the Task model I have an assigned_by column and an assigned_to column, so that anyone can create a new task and assign it to anyone else. Later I want to be able for each User to view all tasks assigned to them and all tasks they have assigned to someone else. To do this, I want to setup an association. Is it okay to do something like this?
class Task < ApplicationRecord
belongs_to :user, :foreign_key => 'assigned_by'
belongs_to :user, :foreign_key => 'assigned_to'
end
Where I have two foreign keys in the same model. Then in the User model I have:
class User < ApplicationRecord
has_many :tasks
end
Is this the proper way to do something like this?
What you probably want is to setup three tables:
class User < ApplicationRecord
has_many :assignments, foreign_key: 'assignee_id'
has_many :assignments_as_assigner, foreign_key: 'assignee_id'
has_many :tasks, through: :assignments
has_many :assigned_tasks, through: :assignments_as_assigner
has_many :created_tasks, class_name: 'Task'
foreign_key: 'creator'
end
class Task < ApplicationRecord
belongs_to :creator, class_name: 'User'
has_many :assignments
end
class Assignment < ApplicationRecord
belongs_to :assignee, class_name: 'User'
belongs_to :assigner, class_name: 'User'
belongs_to :task
end
This creates a one to many association so that a task can be assigned to many users.
Each association in the model has to have a unique name - otherwise you will overwrite the previous association.
The approach you suggested will not work as you can't define 2 methods with the same name (in this case both will be called user).
A better way would be calling the relation by what it actually.
For example
class Task < ApplicationRecord
belongs_to :assigned_by, class_name: 'User'
belongs_to :assigned_to, class_name: 'User'
end
You may also need to add a foreign_key option or call the foreign key in the DB assigned_by_id and assigned_to_id
Also, you will need to change your User model as tasks method is ambiguous.
class User < ApplicationRecord
has_many :tasks_delegated, foreign_key: 'assigned_by_id', class_name: 'Task'
has_many :tasks_assigned, foreign_key: 'assigned_to_id', class_name: 'Task'
end
Try to do like this to prevent overwriting.
class User < ApplicationRecord
has_many :owned_tasks, class_name: "Task", foreign_key: "owner_id"
has_many :assigned_tasks, class_name: "Task", foreign_key: "assignee_id"
end
class Task < ApplicationRecord
belongs_to :owner, class_name: "User", foreign_key: "owner_id"
belongs_to :assignee, class_name: "User", foreign_key: "assignee_id"
end

Multiple Associations With the Same Table rails

i have two class User and Bug there are two foreign keys in bug which are referencing to user_id ..the problem is that how i store user_id in foreign key column while creating the record.like for example if user enter bug then his id store in buger_id colunm.
class Bug
belongs_to :buger, class_name: "User", foreign_key: "buger_id"
belongs_to :developer , class_name: "User", foreign_key: "developer_id"
class user
has_many :created_bugs, class_name:"bugs"
has_many :developed_bugs, class_name:"bugs"
You need to add the foreign_key to the has_many declaration!
class User < ActiveRecord::Base
has_many :created_bugs, class_name: 'Bug' , foreign_key: :buger_id
has_many :developed_bugs, class_name: 'Bug' , foreign_key: :developer_id
end
class Bug < ActiveRecord::Base
belongs_to :buger, class_name: 'User'
belongs_to :developer , class_name: 'User'
end
See also: http://guides.rubyonrails.org/association_basics.html
You can specify class and foreign key on the has_many line as well.
has_many :created_bugs, class_name:"Bug", foreign_key: 'buger_id'
has_many :developed_bugs, class_name:"Bug", foreign_key: 'developer_id'
In Rails 5.1 or greater you can do it like this:
Migration
class CreateBug < ActiveRecord::Migration
def change
ccreate_table(:bugs) do |t|
t.references :bugger, foreign_key: { to_table: 'users' }
t.references :developer, foreign_key: { to_table: 'users' }
end
end
end
This will create the fields bugger_id, and developer_id and make the database level references to the users table
Models
class Bug < ActiveRecord::Base
belongs_to :bugger, class_name: "User"
belongs_to :developer, class_name: "User"
end
class User < ActiveRecord::Base
has_many :created_bugs, class_name: "Bug", foreign_key: "bugger_id"
has_many :developed_bugs, class_name: "Bug", foreign_key: "developer_id"
end
FactoryBot
If you use FactoryBot then your factory might look something like this:
FactoryBot.define do
factory :bug do
association :bugger, factory: :user
association :developer, factory: :user
end
end

Rails set role based relationship between belong_to and has_many for multiple primary keys

My belong_to Item -> User relationship works; however, how do I setup the corresponding relationship in my User model (has many User -> Item)?
#item.rb
belongs_to :update_user, foreign_key: :item_updated_at_user_id, class_name: "User"
belongs_to :delete_user, foreign_key: :item_deleted_at_user_id, class_name: "User"
#user.rb
has_many :update_items, class_name: "Items", inverse_of: :update_user
has_many :delete_items, class_name: "Items", inverse_of: :delete_user
The associations should look as below:
class Item < ActiveRecord::Base
belongs_to :update_user, foreign_key: :item_updated_at_user_id, class_name: "User", inverse_of: :update_items
belongs_to :delete_user, foreign_key: :item_deleted_at_user_id, class_name: "User", inverse_of: :delete_items
end
class User < ActiveRecord::Base
has_many :update_items, foreign_key: :item_updated_at_user_id, class_name: "Item", inverse_of: :update_user
has_many :delete_items, foreign_key: :item_deleted_at_user_id, class_name: "Item", inverse_of: :delete_user
end
class_name: "Items" should be class_name: "Item"(Note: Model names are Singular)
Specify foreign key option on both side of association.
Also, Its best to specify inverse_of option on both sides of association.

how can I do self-reference with ruby on rails?

I want to self-referentiate a model in a RoR app but, I don't know exactly how. I want to save a linked list where the next node has the id of the previous one. how can I do this rails way? It is a one-to-one relation.
The easiest way:
class MyModel < ActiveRecord::Base
belongs_to :parent, :class_name => 'MyModel'
has_many :children, :class_name => 'MyModel', :foreign_key => 'parent_id'
end
rails 5
add column xxx_id in users table:
in migration file:
add_reference :users, :xxx, index: true
and add code in User model
has_many :users, class_name: 'User', foreign_key: 'xxx_id'
belongs_to :manager, class_name: 'User', foreign_key: 'xxx_id'
If you don't have a manager for every user, you need to add optional: true.
'foreign_key' is not necessary. By default this is guessed to be the name of this class in lower-case and “_id” suffixed.
if foreign_key is user_id, user don't have manager necessary.
the result is:
has_many :users, class_name: 'User'
belongs_to :manager, class_name: 'User', optional: true
They are called self-joins
I've spent some time trying to make it work using Rails 3.2.14
The documentation's suggestion for self-joining associations hasn't worked for belongs_to associations. Adding a foreign key fixed the issue.
Class User < ActiveRecord::Base
has_many :invitees, class_name: 'User', foreign_key: :invited_by
belongs_to :host, class_name: 'User', foreign_key: :invited_by
end

Resources