Assume we have this relationship:
class Company < ActiveRecord::Base
has_one :company_address, class_name: 'Address', foreign_key: 'company_address_id'
has_one :overseas_address, class_name: 'Address', foreign_key: 'overseas_address_id'
end
If I pry within a new Address instance, is it possible to return :company_address or :overseas_address?
Fields are the same, but I want to do different validations.
Yes, there are alternative ways of solving the problem, but I'm more interested into whether finding the name of the relationship is actually possible.
Thanks!
You should have corresponding belong_to: in your Address model, then try Address.reflect_on_all_associations(belongs_to)
it will return a collection of ActiveRecord::Reflection::AssociationReflection which will have different name (depending on your belong_to)
In order to check instance you should mark your belong_tos as inverse_of: :put_corresponding_association, your has_one as inverse_of belong_tos and then you can check corresponding methods (generated by belong_tos) for presence of parent Company object
The idea is assume an Address as
class Address < ActiveRecord::Base
belongs_to :company, class_name: 'Company', foreign_key: 'company_address_id', inverse_of: :company_address
belongs_to :oversea, class_name: 'Company', foreign_key: 'overseas_address_id', inverse_of: :overseas_address
end
class Company < ActiveRecord::Base
has_one :company_address, class_name: 'Address', foreign_key: 'company_address_id', inverse_of: :company
has_one :overseas_address, class_name: 'Address', foreign_key: 'overseas_address_id', inverse_of: :oversea
end
and you create address as address = company.overseas_address.new
then
address.company == nil
address.oversea == company
Related
Wondering about a relationship I have and not sure wheter this is due to cause some issues in the future.
I have the following relationships with Users and Leases.
class User < ApplicationRecord
has_one :lease, foreign_key: "tenant_id"
has_many :leases, foreign_key: "landlord_id"
end
and
class Lease < ApplicationRecord
belongs_to :tenant, class_name: "User"
belongs_to :landlord, class_name: "User"
end
and I'm trying to convert the relationship with the tenant and the lease to has_many, but I don't know how to approach this the right way.
I got this to work with
class User < ApplicationRecord
has_many :leases_as_landlord, class_name: "Lease", foreign_key: "tenant_id"
has_many :leases_as_tenant, class_name: "Lease", foreign_key: "landlord_id"
end
and
class Lease < ApplicationRecord
belongs_to :tenant, class_name: "User", inverse_of: :leases_as_tenant
belongs_to :landlord, class_name: "User", inverse_of: :leases_as_landlord
end
but I don't like calling User.leases_as_landlord and User.leases_as_tenant. What I would like to do is just call User.leases to return the leases in which the User is either the landlord or the tenant.
You can add instance method:
class User < ApplicationRecord
has_many :leases_as_landlord, class_name: "Lease", foreign_key: "tenant_id"
has_many :leases_as_tenant, class_name: "Lease", foreign_key: "landlord_id"
def leases
leases_as_landlord.or(leases_as_tenant)
end
end
It will also return ActiveRecord_AssociationRelation and you can chain other ActiveRecord method on it.
Also I would recommend to follow Rails Convention and name your has_many associations in the plural.
class User < ApplicationRecord
has_many :landlord_leases, class_name: 'Lease', foreign_key: :tenant_id
has_many :tenant_leases, class_name: 'Lease', foreign_key: :landlord_id
def leases
landlord_leases.or(tenant_leases)
end
end
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
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
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.
I am using some Message model with a content and sender and receiver, so I put
attr_accessible :sender_id
attr_accessible :receiver_id
attr_accessible :content
belongs_to :user
in my User model
has_many :messages, foreign_key: 'sender_id', class_name: "Message"
has_many :pushes, foreign_key: 'receiver_id', class_name: "Message"
First, is this a best practice ?
Btw, suppose I have some message instance, how can I get in a rails way the receiver and the sender instance, without writing User.find(message.sender_id) or `User.find(message.receiver_id). (Btw, I know I can use some table and use has_many through mecanism)
Your user model seems to be setup properly.
Regarding your second question, you can do this:
class Message < ActiveRecord::Base
belongs_to :sender, class_name: 'User'
belongs_to :receiver, class_name: 'User'
end
That way, both message.sender and message.receiver will give you a user object.
You're on the right track.
I have this in my current app:
class Message < ActiveRecord::Base
belongs_to :to, class_name: User
belongs_to :from, class_name: User
end
Given an instance of a Message, I just do, for example:
if message.to == current_user
...
The relevant part of my User class:
class User < ActiveRecord::Base
has_many :messages, foreign_key: :to_id # These are received messages
has_many :sent_messages, foreign_key: :from_id, class_name: Message