ActionRecord modeling: Employee with many managers - ruby-on-rails

This should be easy. I think I must be getting caught up on naming.
Both a 'manager' and a 'subordinate' (employee) are of class "Person".
Here's what I have:
class Person < ActiveRecord::Base
has_many :person_manager_assignments
has_many :managers, :through => :person_manager_assignments
has_many :subordinates, :through => :person_manager_assignments
end
class PersonManagerAssignment < ActiveRecord::Base
has_one :subordinate, :class_name => "Person", :foreign_key => "id", :primary_key => 'person_id'
has_one :manager, :class_name => "Person", :foreign_key => "id", :primary_key => 'manager_id'
end
Which works great for checking and assigning managers.
I'm caught on the part about subordinates. It returns the Person's self, instead of their subordinates:
p.subordinates
Person Load (0.5ms) SELECT "people".* FROM "people" INNER JOIN "person_manager_assignments" ON "people"."id" = "person_manager_assignments"."person_id" WHERE "person_manager_assignments"."person_id" = 15973
See the bit where in the WHERE clause where it's matching "person_id"? I need that to be "manager_id", but messing with the PersonManagerAssignment associations foreign_key and primary_key values doesn't seem to help.
Any ideas?

Answer is essentially here: http://railscasts.com/episodes/163-self-referential-association
So I think you need this:
class Person < ActiveRecord::Base
has_many :person_manager_assignments
has_many :managers, :through => :person_manager_assignments
has_many :subordinate_relationships, :class_name=>"PersonManagerAssignment", :foreign_key=>"manager_id"
has_many :subordinates, :through => :subordinate_relationships, :source=>:person
end
and
class PersonManagerAssignment < ActiveRecord::Base
belongs_to :person
belongs_to :manager, :class_name=>"Person"
end
Rock on.

I'm going to guess that your PersonManagerAssignment table has a person_id and a manager_id and associations in the model like has_one :person and has_one :manager. If that's the case, I'll recommend changing this association
has_one :person
to this
has_one :subordinate, :class_name => "Person", :foreign_key => "person_id"
and then your has_many :subordinates should work as expected.

class Person < ActiveRecord::Base
has_many :subordinates :through => :person_manager_assignments
has_many :managers, :through => :person_manager_assignments
end
class PersonManagerAssignment < ActiveRecord::Base
belongs_to :subordinate, :class_name => 'Person'
belongs_to :manager, :class_name => 'Person'
end

Related

Rails: How to create a has_many through association with an alias/class_name

I am trying to transform my HABTM to has_many through relations. Sometimes I have to connect the same models in different ways. For example to specify different roles for authors.
With a HABTM I would do this through the declaration of a class_name option. Just like:-
class Project < ActiveRecord::Base
has_and_belongs_to_many :curators, :class_name => :author, :through => :projects_curators
end
class ProjectsCurator < ActiveRecord::Base
attr_accessible :project_id, :author_id
belongs_to :project
belongs_to :author
end
class Author < ActiveRecord::Base
has_and_belongs_to_many :projects, :through => :projects_curators
end
But when I transform everything into a has_many through:
class Project < ActiveRecord::Base
has_many :project_curators
has_many :curators, :class_name => :author, :through => :project_curators
end
class ProjectCurator < ActiveRecord::Base
attr_accessible :project_id, :author_id
belongs_to :project
belongs_to :author
end
class Author < ActiveRecord::Base
has_many :project_curators
has_many :projects, :through => :project_curators
end
I get: Could not find the source association(s) :curator or :curators in model ProjectCurator. Try 'has_many :curators, :through => :project_curators, :source => <name>'. Is it one of :author or :project?
When I add :source
has_many :curators, :class_name => :author, :through => :project_curators, :source => :author
I get:
uninitialized constant Project::author
How can I get this working? Thank you very much in advance!
Understanding :source option of has_one/has_many through of Rails should help you out
When you declare the source you're not declaring the class of the has_many relationship, but the name of the relationship you're using as the middle man. In your case, try:
has_many :curators, :through => :project_curators, :source => :author
As someone in the above post states:
Most simple answer - the name of the relationship in the middle

Joining one table two times

My db schema:
tournaments(id, ...)
teams(tournament_id, id, ...)
matches(tournament_id, id, team_id_home, team_id_away, ...)
Models:
class Tournament < ActiveRecord::Base
has_many :teams, dependent: :destroy
has_many :matches, dependent: :destroy
...
end
class Team < ActiveRecord::Base
belongs_to :tournament
...
end
class Match < ActiveRecord::Base
belongs_to :tournament
has_many :teams
...
end
I would like to have the following data in my view:
match_id team_id_home team_id_away team_id_home_name team_id_away_name
So, I'm asking for help with the following query (I'm trying to get team's names, but having problem with joining):
#matches = #tournament.matches.where(:tournament => #tournament).joins(:teams).paginate(page: params[:page])
I'm fairly new to rails, but you should be able to setup your associations like this: (going from memory)
class Match < ActiveRecord::Base
belongs_to :tournament
has_one :home_team, :class_name => "Team", :foreign_key => "team_id_home"
has_one :away_team, :class_name => "Team", :foreign_key => "team_id_away"
end
#####
m = Match.first
m.away_team.team_name
m.home_tam.team_name
Or in your case:
#matches = #tournament.matches.paginate(page: params[:page])
I don't think you need the where function: the has_many association tells rails to only pull matching matches.
It is belongs_to, not has_one in Match model.
class Match < ActiveRecord::Base
belongs_to :tournament
belongs_to :home_team, :class_name => "Team", :foreign_key => "team_id_home"
belongs_to :away_team, :class_name => "Team", :foreign_key => "team_id_away"
end
class Team < ActiveRecord::Base
belongs_to :tournament
has_many :matches
end
Now I can use tournament.home_team.name in my view

Rails polymorphic table that has other kinds of associations

I'm currently modeling a Rails 3.2 app and I need a polymorphic association named "archivable" in a table named "archives". No worries with it, but my "archives" table must also belongs_to a "connections" table. I just want to know if there's any constraints from Rails to do that.
You can see the model here
Another detail, my Connection model has twice user_id as foreign key. A user_id as sender and a user_is as receiver. Possible? I think what I did below won't work...
Here are my models associations.
class User < ActiveRecord::Base
has_many :connections, :foreign_key => :sender
has_many :connections, :foreign_key => :receiver
end
class Connections < ActiveRecord::Base
belongs_to :user
has_many :archives
end
class Archive < ActiveRecord::Base
belongs_to :connection
belongs_to :archivable, :polymorphic => true
end
class Wink < ActiveRecord::Base
has_many :archives, :as => :archivable
end
class Game < ActiveRecord::Base
has_many :archives, :as => :archivable
end
class Message < ActiveRecord::Base
has_many :archives, :as => :archivable
end
Do you see anything wrong or something not doable with Rails?
Thank you guys.
I think you want to do this :
class Connections
belongs_to :sender, :class_name => 'User', :foreign_key => 'sender_id'
belongs_to :receiver, :class_name => 'User', :foreign_key => 'receiver_id'
end
class User
has_many :sended_connections, :class_name => 'Connection', :as => :sender
has_many :received_connections, :class_name => 'Connection', :as => :receiver
end
Important : Don't declare 2 times has_many :connections with the same name !

Rails Model has_many with multiple foreign_keys

Relatively new to rails and trying to model a very simple family "tree" with a single Person model that has a name, gender, father_id and mother_id (2 parents). Below is basically what I want to do, but obviously I can't repeat the :children in a has_many (the first gets overwritten).
class Person < ActiveRecord::Base
belongs_to :father, :class_name => 'Person'
belongs_to :mother, :class_name => 'Person'
has_many :children, :class_name => 'Person', :foreign_key => 'mother_id'
has_many :children, :class_name => 'Person', :foreign_key => 'father_id'
end
Is there a simple way to use has_many with 2 foreign keys, or maybe change the foreign key based on the object's gender? Or is there another/better way altogether?
Thanks!
Found a simple answer on IRC that seems to work (thanks to Radar):
class Person < ActiveRecord::Base
belongs_to :father, :class_name => 'Person'
belongs_to :mother, :class_name => 'Person'
has_many :children_of_father, :class_name => 'Person', :foreign_key => 'father_id'
has_many :children_of_mother, :class_name => 'Person', :foreign_key => 'mother_id'
def children
children_of_mother + children_of_father
end
end
To improve on Kenzie's answer, you can achieve an ActiveRecord Relation by defining Person#children like:
def children
children_of_mother.merge(children_of_father)
end
see this answer for more details
Used named_scopes over the Person model
do this:
class Person < ActiveRecord::Base
def children
Person.with_parent(id)
end
named_scope :with_parent, lambda{ |pid|
{ :conditions=>["father_id = ? or mother_id=?", pid, pid]}
}
end
I believe you can achieve the relationships you want using :has_one.
class Person < ActiveRecord::Base
has_one :father, :class_name => 'Person', :foreign_key => 'father_id'
has_one :mother, :class_name => 'Person', :foreign_key => 'mother_id'
has_many :children, :class_name => 'Person'
end
I'll confirm and edit this answer after work ; )
My answer to Associations and (multiple) foreign keys in rails (3.2) : how to describe them in the model, and write up migrations is just for you!
As for your code,here are my modifications
class Person < ActiveRecord::Base
belongs_to :father, :class_name => 'Person'
belongs_to :mother, :class_name => 'Person'
has_many :children, ->(person) { unscope(where: :person_id).where("father_id = ? OR mother_id = ?", person.id, person.id) }, class_name: 'Person'
end
So any questions?
I prefer to use scopes for this issue. Like this:
class Person < ActiveRecord::Base
belongs_to :father, :class_name => 'Person'
belongs_to :mother, :class_name => 'Person'
has_many :children_of_father, :class_name => 'Person', :foreign_key => 'father_id'
has_many :children_of_mother, :class_name => 'Person', :foreign_key => 'mother_id'
scope :children_for, lambda {|father_id, mother_id| where('father_id = ? AND mother_id = ?', father_id, mother_id) }
end
This trick make it easy to get children without use instances:
Person.children_for father_id, mother_id
Not a solution to the general question as stated ("has_many with multiple foreign keys"), but, given a person can either be a mother or a father, but not both, I would add a gender column and go with
has_many :children_of_father, :class_name => 'Person', :foreign_key => 'father_id'
has_many :children_of_mother, :class_name => 'Person', :foreign_key => 'mother_id'
def children
gender == "male" ? children_of_father : children_of_mother
end
I was looking for the same feature, if you don't want to return an array but a ActiveRecord::AssociationRelation, you can use << instead of +.
(See the ActiveRecord documentation)
class Person < ActiveRecord::Base
belongs_to :father, :class_name => 'Person'
belongs_to :mother, :class_name => 'Person'
has_many :children_of_father, :class_name => 'Person', :foreign_key => 'father_id'
has_many :children_of_mother, :class_name => 'Person', :foreign_key => 'mother_id'
def children
children_of_mother << children_of_father
end
end

HABTM 2 tables 2 different relationships

I have a Service Types table containing id and name of a couple of dozen services.
I have a Projects table that has to have a list of Proposed Services, and a list of Accepted Services.
I know that I would use HABTM on both sides with a project_service_types table in between.
I can't quite figure out what to do when I have 2 different relationships between the same table. I suspect it uses the :join_table and :associated_forign_key, but I can't get it to work in my app.
thanks.
I solved it using HABTM...
class ServiceType < ActiveRecord::Base
has_and_belongs_to_many :accepted_projects, :class_name => "Project", :join_table => :projects_accepted_types
has_and_belongs_to_many :proposed_projects, :class_name => "Project", :join_table => :projects_proposed_types
end
class Project < ActiveRecord::Base
has_and_belongs_to_many :accepted_types, :class_name => "ServiceType", :join_table => :projects_accepted_types
has_and_belongs_to_many :proposed_types, :class_name => "ServiceType", :join_table => :projects_proposed_types
end
While you can solve this with habtm, what you're talking about is the use-case for has_many :through. You want to attach a bit of information along with the relationship. To do this you create a join model that represents the relationship.
In the end this allows you to treat your service proposal as a first-class "thing" in your domain. When the service is accepted you can just change the status. This also saves a join.
Migration
create_table :project_services do |t|
t.references :project
t.references :service_type
t.string :status
end
Models
class ProjectService < ActiveRecord::Base
belongs_to :project
belongs_to :service
end
class Project < ActiveRecord::Base
has_many :project_services
has_many :accepted_services, :through => :project_services,
:conditions => { :status => 'accepted' }
has_many :proposed_services, :through => :proposed_services,
:conditions => { :status => 'proposed' }
end
class Service < ActiveRecord::Base
has_many :project_services
has_many :accepted_projects, :through => :project_services,
:conditions => { :status => 'accepted' }
has_many :proposed_projects, :through => :proposed_services,
:conditions => { :status => 'proposed' }
end
For that you'd probably want to use has_many :through as in:
class ProposedService < ActiveRecord::Base
belongs_to :project
belongs_to :service_type
class AcceptedService < ActiveRecord::Base
belongs_to :project
belongs_to :service_type
class Projects < ActiveRecord::Base
has_many :proposed_services
has_many :accepted_services
has_many :service_types, :through => :proposed_services
has_many :service_types, :through => :accepted_services
class ServiceTypes < ActiveRecord::Base
has_many :proposed_services
has_many :accepted_services
has_many :projects, :through => :proposed_services
has_many :projects, :through => :accepted_services
The many-to-many section here:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
explains this in more detail. Hope this helps!

Resources