how can I do self-reference with ruby on rails? - 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

Related

Has_one relation with a specific foreign_key

In my Task model, I would like to create two has_one relation with the pricer model. To do that, I added the value :client_pricer_id and :presta_pricer_id to my Task table. And now, I want to create an has_one for each of them
My code :
Migration file :
class AddPricerToTasks < ActiveRecord::Migration[6.0]
def change
add_column :tasks, :client_pricer_id, :integer, foreign_key: true
add_column :tasks, :presta_pricer_id, :integer, foreign_key: true
end
end
Task model
has_one :client_pricer, :class_name => 'Pricer', :foreign_key => 'client_pricer_id'
has_one :presta_pricer, :class_name => 'Pricer', :foreign_key => 'presta_pricer_id'
View :
#task.client_pricer
Error :
SQLException: no such column: pricers.presta_pricer_id
I certainly forgot to specify a variable in my has_one line. But I don't know which one :)
You want to use belongs_to not has_one.
class Task < ApplicationRecord
belongs_to :client_pricer, class_name: 'Pricer', inverse_of: :client_pricer_tasks
belongs_to :presta_pricer, class_name: 'Pricer', inverse_of: :presta_pricer_tasks
end
class Pricer < ApplicationRecord
has_many :client_pricer_tasks, class_name: 'Task', foreign_key: :client_pricer_id
has_many :presta_pricer_tasks, class_name: 'Task', foreign_key: :presta_pricer_id
end
This is a really common mixup due to the confusing semantics.
belongs_to places the foreign key on this models table. has_one places it on the other end.
The inverse of has_one/has_many is always a belongs_to. Otherwise the relations just point at each other in a circle.
I finally found my error have to change the has_one to belongs_to (because the key is into my Task model )
My code :
Task model :
belongs_to :client_pricer, :class_name => 'Pricer', optional: true
belongs_to :presta_pricer, :class_name => 'Pricer', optional: true

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

Self Referencing ActiveRecord Model

I just started learning Rails. I have the following question.
I generated "User" models (table "users") and I want to add relation to manager. Manager is also a User. I want to be able to write:
user.manager # => returns User object
I tried to write it like this:
class User
belongs_to :user, as: :manager
end
But it didn't work. Can you please help me.
If Manager is referenced by manager_id field, then:
class User
belongs_to :manager, class_name: 'User'
end
You can also identify subordinates of the user using has_many relation:
class User
belongs_to :manager, class_name: 'User'
has_many :subordinates, class_name: 'User', foreign_key: 'manager_id'
end
belongs_to :manager, class_name: 'User', foreign_key: 'manager_id'
Try this

Foreign Key class_name

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.

Has_many through with multiple relationships between the entities

I have these model definitions:
class Property < ActiveRecord::Base
belongs_to :user
has_many :users_favourites_properties, class_name: 'UsersFavouritesProperties', dependent: :destroy, :foreign_key => :property_id
has_many :favourites, :class_name => "User", through: :users_favourites_properties
end
class User < ActiveRecord::Base
has_many :properties
has_many :users_favourites_properties, class_name: 'UsersFavouritesProperties', dependent: :destroy, :foreign_key => :user_id
has_many :favourites, class_name: "Property", through: :users_favourites_properties
end
class UsersFavouritesProperties < ActiveRecord::Base
self.table_name = "favourites"
belongs_to :favourites, class_name: 'User', :foreign_key => :user_id, :primary_key => :id
belongs_to :favourites, class_name: 'Property', :foreign_key => :property_id, :primary_key => :id, counter_cache: :users_count
end
When I execute:
current_user.favourites << property
The UsersFavouritesProperties object created has the same user_id and property_id because the user_id is beign setted with the property_id.
Do you know what is happening?
You seem to misunderstand what the association name is for.
Each association method call (like has_many) adds certain methods to a model that allow for easy lookup for associated objects.
The name of association means (in scope of a model) "what is it for me". A symbol you specify first is an association name, and it is used (among other things) as a name of a method that you will use to access that association.
By defining an association named favourites once, you've defined the methods for UsersFavouritesProperties. The second time you define an association with the same name, some of the first one is redefined and crazy things can happen.
So, following that "what is it for me" principle, you can figure that a User associated with a certain UsersFavouritesProperty is UsersFavouritesProperty's user, and the resulting association, that you have:
belongs_to :favourites, class_name: 'User', :foreign_key => :user_id, :primary_key => :id
should be more like simple:
belongs_to :user
The rest is not even needed, as it follows the Rails' convention: class name and foreign key can be derived from association name in standard ways (capitalize for class name, add _id for foreign key), primary key is default.
I strongly recommend you read the Rails Style Guide and let ActiveRecord guess the most you can provide.
These lines
belongs_to :favourites, ...
belongs_to :favourites, ...
Should be
belongs_to :user, ...
belongs_to :property, ...
You can't use two keys for belongs_to

Resources