Rails: How can this be done with a has_one association? - ruby-on-rails

My model has two columns, one named foo_id and the other bar_id.
I'm wondering if it's possible to turn these two simple methods into has_one associations:
class SomeModel < ActiveRecord::Base
def foobar_foo
Foobar.find( self.foo_id )
end
def foobar_bar
Foobar.find( self.bar_id )
end
end
Perhaps I've been staring at the documentation for too long, but I can't seem to find a way to tell Rails to use self.foo_id as the foreign key for the other model.
I'm aware that in most cases this should instead be a has_many :through or maybe a belongs_to, but for the sake of argument I'm interested to learn if this is possible with a has_one

Does this help?
has_one :foo, class_name: "Foobar", foreign_key: :foo_id
has_one :bar, class_name: "Foobar", foreign_key: :bar_id
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_one

Finally had a chance to revisit this problem and incidentally, stumbled across a solution within the first few minutes of trying..
has_one :bar, primary_key: :bar_id, foreign_key: :id, class_name: 'Foobar'
has_one :foo, primary_key: :foo_id, foreign_key: :id, class_name: 'Foobar'
Works as intended.

Related

Association with proc for mongoid

I saw in sources of Mongoid that no proc can be send to association method, what is best practice to achieve below AR association with Mongoid:
class Task
...
belongs_to :creator, ->{where(type: :manager)}, class_name: "User"
belongs_to :acceptor, ->{where(type: :acceptor)}, class_name: "User"
end
It seems I'm find answer. Relations methods in Mongoid accepts block as third argument.
belongs_to :creator, class_name: "User", inverse_of: :created_tasks do
->{ where(type: :manager)}
end
belongs_to :executor, class_name: "User", inverse_of: :accepted_tasks do
->{where(type: :acceptor)}
end
At least for has_many relations I've been having trouble getting this working with a default filter in mongo, so I wonder if your answer is actually working correctly for belongs_to. This has been working correctly on the has_many side for me so I figured it might be a useful answer for someone else as well:
belongs_to :user do
def creator
where(type: :manager)}
end
def executor
where(type: :acceptor)
end
end
They can then be accessed by saying task.user.accepted, task.user.created, etc, however I have been unable to figure out how to actually set the default filtering for the overall relation.

Using includes on a custom association in rails

I have a class Team that has many Matchups
class Team < ActiveRecord::Base
has_many(
:home_matchups,
class_name: "Matchup",
foreign_key: :team_1_id,
primary_key: :id
)
has_many(
:away_matchups,
class_name: "Matchup",
foreign_key: :team_2_id,
primary_key: :id
)
def matchups
Matchup.where("team_1_id = ? OR team_2_id = ?", id, id)
end
end
and
class Matchup < ActiveRecord::Base
validates :team_1_id, :team_2_id, :week, presence: true
belongs_to(
:team_1,
class_name: "Team",
foreign_key: :team_1_id,
primary_key: :id
)
belongs_to(
:team_2,
class_name: "Team",
foreign_key: :team_2_id,
primary_key: :id
)
has_one :league, through: :team_1, source: :league
end
This all works fine, until I want to use includes like this: Team.includes(:matchups) because although matchups returns an active record relation I can't use it. This is a problem later because I need this kind of information for many teams and I don't want to make a query for every team. Can anyone tell me how I could use includes to get the combination?
Like Sean Huber said, include the home and away matchups, then refer to them with .matchups
def matchups
(home_matchups + away_matchups).sort_by(&:week)
end
It's a little slower (maybe), but you'll be guaranteed to use the relation if it's loaded.
The general approach here - include the relations like you would, then use convenience methods that refer to those relations - whether it's in a view or another model method like this example.

how to setup two user_id columns in one model in rails?

I have two models
class User
has_many :disputes
end
class Dispute
belongs_to :user
end
So Dispute has :user_id, but my problem is that a Dispute has 2 sides - the claimant and the indicted, both of which are users.
I tried to solve this problem by creating two columns: :claimant_id and :indicted_id, and passing arguments like #dispute.claimant_id = current_user.id, but after that I can't use a relationship tricks like #dispute.user.name or #user.disputes with my :claimant_id and :indicted_id.
Is there any way to set up two :user_id (like a claimant and an indicted) in one model and still maintain the relationships between Dispute and User models?
You can go the route of having :claimant_id and :indicted_id on your users table, but in your class def you need
class Dispute < ActiveRecord::Base
belongs_to :claimant, class_name: 'User', :foreign_key => :claimant_id
belongs_to :indicted, class_name: 'User', :foreign_key => :indicted_id
end
Reference: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-belongs_to
I think this is a duplicate of Ruby on Rails Double Association
For your case use class_name as the second parameter to your belongs_to function and a hash with foreign_key and the id column name (as pointed out by #devkaoru), like so:
class Dispute < ActiveRecord::Base
belongs_to :claimant, class_name: 'User', :foreign_key => :claimant_id
belongs_to :indicted, class_name: 'User', :foreign_key => :indicted_id
end
I think that should do it up right.
Disclaimer: This is an untested solution

i18n for a has_many through self-referential

I don't pretend to begin understanding i18n and have not had to use it often but here is the concept.
My User class:
class User < ActiveRecord::Base
has_many :parent_relationships, class_name: 'UserRelationship', foreign_key: 'parent_id'
has_many :user_relationships, class_name: 'UserRelationship', foreign_key: 'user_id'
has_many :dealers, through: :parent_relationships
has_one :agent, through: :user_relationships
...
end
My UserRelationships class:
class UserRelationship < ActiveRecord::Base
attr_accessible :end_date, :parent_id, :start_date, :user_id
belongs_to :agent, class_name:'User',conditions:["user_type='Agent' AND id=?",:parent_id]
belongs_to :dealer, class_name:'User',conditions:["user_type='Dealer' AND id=?",:user_id]
end
I want User.model_name.human to return "User" but when the agent association is referenced I would like it to return "Agent".
I have tried a few things none of which have any success and would be less than useful posting here as they were just stabs in the dark. So my question is 2 fold. Is this possible and if so can someone explain the implementation so I have a better understanding of i18n.
My reason for doing this is I have a ransack form which searches Users but I also would like to have it search Agent as a optgroup and right now it outputs 2 User opt groups This github issue seems to suggest it is possible but the poster did not explain the implementation.
I have figured it out ransack uses it's own namespace for associations in translate.rb line 53
defaults = key.blank? ? [:"#{context.klass.i18n_scope}.models.#{context.klass.model_name.singular}"] : [:"ransack.associations.#{context.klass.model_name.singular}.#{key}"]
So the i18n translation would be:
ransack:
associations:
user:
agent: "Agent"
In case anyone else comes across this issue.

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