eager loading with attributes in join model rails - ruby-on-rails

My app uses a :has_many :through association, and I'm having trouble figuring out how to most efficiently load and display data from both ends of the association and the association itself.
Here are my classes:
class Person < ActiveRecord::Base
has_many :people_ranks
has_many :ranks, :through => :people_ranks
has_many :institutions_people
has_many :institutions, :through => :institutions_people
belongs_to :school
belongs_to :department
end
class Institution < ActiveRecord::Base
has_many :institutions_people
has_many :people, :through => :institutions_people
end
class InstitutionsPerson < ActiveRecord::Base
belongs_to :institution
belongs_to :person
end
and their corresponding models:
create_table :people, :force => true do |t|
t.string :name
t.string :degree
t.integer :year_grad
t.integer :year_hired
end
create_table :institutions, :force => true do |t|
t.string :name
t.integer :ischool
end
create_table :institutions_people, :id => false do |t|
t.integer :institution_id
t.integer :person_id
t.string :rel_type
end
I want to show a person's institution info with something like #person.year_hired, #person.institution.name, and #person.institution.institutions_people.rel_type (where rel_type is either "graduated" or "hired:), but I know that third part won't work. Using the following in the show bit in the person_controller:
#person = Person.find(params[:id], :include => [:school, :department, :institutions_people, :people_ranks, {:institutions_people => :institution}, {:people_ranks => :rank}])
gives me access to #person.institutions and #person.institutions_people, but how do I connect the rel_type attribute from the join to the person-institution relationship? (I'm coming from PHP and now how to build the SQL and loop through it there, but RoR has me stumped.)
I've looked for help under "eager loading" and "associations with :has_many :through", but I get answers about building the associations. My question is really about accessing the association's data after it exists. My app uses static data, and I'm not worried about the update, destroy, or create methods. Thank you for your help!

The way to access the data is through the institutions_people association. So, you would do something like:
me = Person.first
rel = me.institutions_people.first
And then in the view
<%= rel.rel_type %> from <%= rel.institution.name %>
Alternatively, you can give yourself a full list of institutions along with their info:
me = Person.first
And then in the view:
<% for ip in me.institutions_people %>
<%= ip.rel_type %> from <%= ip.institution.name %>
<% end %>

Related

associations without the user_id column

I created a FriendRequest.rb model in my Rails app with the following table columns.
create_table "friend_requests", force: true do |t|
t.integer "requesting_user_id"
t.integer "requested_friend_id"
t.datetime "created_at"
t.datetime "updated_at"
end
With relationships defined as you see below, I added this code to a /views/users/show.html.erb page show the friend requests that have been made for each user. However, I'm getting this error
PG::UndefinedColumn: ERROR: column friend_requests.user_id does not exist
because, I obviously didn't create a user_id column. Is there a way that I can make this code work by adding more information to the relationships? or should I scrap my work and do it differently?
<% for user in #user.friend_requests %>
<li><%= h user.name %></li>
<%= link_to "Add Friend", friend_requests_path(:friend_id => user), :method => :post %>
<% end %>
User.rb
has_many :friend_requests
FriendRequest.rb
belongs_to :user
Just change your has_many association for:
has_many :friend_requests, foreign_key: 'requesting_user_id'
By default, Rails will look for [model_name]_id in the other table, this is why it is looking for user_id, but by adding the foerign_key: option, you can override this default behaviour and tell Rails what is the name of the foreign_key you want to use.
You can use this configuration:
class User < ActiveRecord::Base
has_many :friend_requests
has_many :requesters, through: friend_requests
end
class FriendRequest < ActiveRecord::Base
belongs_to :requester, foreign_key: 'requesting_user_id'
belongs_to :requested, foreign_key: 'requested_friend_id'
validates :requester_id, presence: true
validates :requested_id, presence: true
end
Take a look at:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
Look especially for the options :primary_key and :foreign_key

Rails model multiple has_many :through relationships output to json

I have the following data models and would like to render a json hash that includes information from each model. For example, client.id, client.name_first, client, name_last, every workout description for each client and each exercise description for each workout.
class Client < ActiveRecord::Base
belongs_to :account
belongs_to :trainer
has_many :programs
has_many :workouts, :through => :programs
end
class Workout < ActiveRecord::Base
has_many :programs
has_many :clients, :through => :programs
has_many :routines
has_many :exercises, :through => :routines
end
class Exercise < ActiveRecord::Base
has_many :routines
has_many :workouts, :through => :routines
end
My database migrations:
class CreateClients < ActiveRecord::Migration
def change
create_table :clients do |t|
t.integer :account_id
t.integer :trainer_id
t.string :name_first
t.string :name_last
t.string :phone
t.timestamps
end
end
end
class CreateWorkouts < ActiveRecord::Migration
def change
create_table :workouts do |t|
t.string :title
t.string :description
t.integer :trainer_id
t.timestamps
end
end
end
class CreateExercises < ActiveRecord::Migration
def change
create_table :exercises do |t|
t.string :title
t.string :description
t.string :media
t.timestamps
end
end
end
I am able to return the workouts for a particular client:
#client = Client.find(params[:id])
clients_workouts = #client.workouts.select('workouts.*,programs.client_id').group_by(&:client_id)
render json: clients_workouts
And I am able to return the exercises for a particular workout:
#workout = Workout.find(params[:id])
exercises_workouts = #workout.exercises.select('exercises.*, routines.workout_id').group_by(&:workout_id)
render json: exercises_workouts
However, I do not know how to return the data with information from all three tables (Client, Workout, Exercise) included (joined through Programs and Routines). Is this possible? And how is it done?
First, I'm not really sure what's happening in your query:
clients_workouts = #client.workouts.select('workouts.*,programs.client_id').group_by(&:client_id)
Is this not sufficient?
#client.workouts
Now, on to the answer... assuming I'm still following:
ActiveRecord offers a .to_json method, which is what's being implicitly called here. The explicit version would be e.g.
render json: clients_workouts.to_json
Knowing that, you can look up to_json in the api (here's some good documentation even though it shows as deprecated: http://apidock.com/rails/ActiveRecord/Serialization/to_json). But, basically, the answer is to start with the root object -- the client I believe -- and build the included objects and attributes/methods from there in the options hash.
render json: #client.to_json(include: { workouts: { include: :exercises } })
You can customize which attributes or methods are included from each related model if needed, just dig into the documentation a little. Have fun!
Very possible and their are different ways to optain this.
One, without any 3rd party library is to use includes, just as if you were solving an n+1 problem or…
Use a much cooler approach and use active model serializers
Active Model Serializers

Trouble with self referential model in Rails

I have a model named User and I want to be able to self reference other users as a Contact. In more detail, I want a uni-directional relationship from users to other users, and I want to be able to reference an owned user of one user as a 'contact'. ALSO, i want to have information associated with the relationship, so I will be adding fields to the usercontact relation (I just edited this sentence in).
I attempted to do this while using the answer to this question as a guide.
Here is the User model:
user.rb
class User < ActiveRecord::Base
attr_accessible(:company, :email, :first_name, :last_name,
:phone_number, :position)
has_many(:user_contacts, :foreign_key => :user_id,
:dependent => :destroy)
has_many(:reverse_user_contacts, :class_name => :UserContact,
:foreign_key => :contact_id, :dependent => :destroy)
has_many :contacts, :through => :user_contacts, :source => :contact
end
I also created the model UserContact as a part of connecting contacts to users:
usercontact.rb
class UserContact < ActiveRecord::Base
belongs_to :user, :class_name => :User
belongs_to :contact, :class_name => :User
end
Here is the create_users.rb migration file i used:
create_users.rb
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :phone_number
t.string :email
t.string :company
t.string :position
t.timestamps
end
end
end
And here is the create_users_contacts.rb migration:
create_users_contacts.rb
class CreateUsersContacts < ActiveRecord::Migration
def up
create_table :users_contacts, :force => true do |t|
t.integer :user_id, :null => false
t.integer :contact_id, :null => false
t.boolean :update, :null => false, :default => false
end
# Ensure that each user can only have a unique contact once
add_index :users_contacts, [:user_id, :contact_id], :unique => true
end
def down
remove_index :users_contacts, :column => [:user_id, :contact_id]
drop_table :users_contacts
end
end
However, for reasons unknown to me, I believe something has gone awry in the linking since on my users index page, I have a column using <td><%= user.contacts.count %></td>, but I get this error from the line when I attempt to load the page:
uninitialized constant User::UserContact
I think the issue may be something to do with the fact that I want to name users associated with another user as contacts, because I cannot find other examples where that is done, and as far as I can tell I am doing everything properly otherwise (similarly to other examples).
The closest similar problem that I found was outlined and solved in this question. The issue was incorrect naming of his connecting model, however I double checked my naming and it does not have that asker's problem.
Any help is appreciated, let me know if any other files or information is necessary to diagnose why this is occurring.
EDIT
After changing usercontact.rb to user_contact.rb, I am now getting this error:
PG::Error: ERROR: relation "user_contacts" does not exist
LINE 1: SELECT COUNT(*) FROM "users" INNER JOIN "user_contacts" ON "...
^
: SELECT COUNT(*) FROM "users" INNER JOIN "user_contacts" ON "users"."id" = "user_contacts"."contact_id" WHERE "user_contacts"."user_id" = 1
EDIT TWO
The issue was that my linking table, users_contacts, was misnamed, and should have been user_contacts! so I fixed it, and now it appears to work!!
You need to rename your usercontact.rb to user_contact.rb
This is naming convention rails autoload works with.

has_many children and has_many parents

I'm trying to figure out a complex relation between a Model.
I have a model called "Concept", which has two inheriting types called "Skill" and "Occupation". Basicly this means that each concept represents a category, but a concept can also be a skill or an occupation when going deep enough into the hierychal tree.
I'm solving this hierachy by using STI. So my schema for the Concepts table looks like this:
class CreateConcepts < ActiveRecord::Migration
def self.up
create_table :concepts do |t|
t.string :uri, :null => false, :length => 255
t.string :type, :null => true, :length => 255
t.integer :isco_code, :null => true
t.timestamps
end
end
def self.down
drop_table :concepts
end
end
The type column determins whether the Concept is a real "Concept" or a "Skill"/"Occupation".
The problem now however the following relations:
EDIT:
A Concept can belong to a single parent Concept
An Occupation can belong to a single parent Concept
A Skill can belong to multiple parent Concepts
A skill has no children
An occupation has no children
so basicly you'd have something like this:
> concept1
> concept2 concept3
> concept4 concept5 concept6 concept7 skill1
> occup1 skill2 occup2 skill5
> occup7 skill2 occup3 skill4
> occup4 skill1 occup8
I hope the picture is a bit clear what I'm trying to explain.
Currently I have created the following migration to try to solve the parent-child relation but I'm not sure how to map this with the associations...
class CreateConceptLinks < ActiveRecord::Migration
def self.up
create_table :concept_links do |t|
t.integer :parent_id, :null => false
t.integer :child_id, :null => false
t.timestamps
end
end
def self.down
drop_table :concept_links
end
end
What I want to end up with is the following posssibilities:
concepta.parents => a Concept object
conceptb.children => an array of Conept objects
Occupation.parents => a Concept object
Occupation.children => []
Skill.parents => an array of Concept objects
Skill.children => []
Hope this is even possible...
You can model hierarchical relations in rails. You've got most of the way there with your migrations. Adding the relations below should allow you to do the method calls you'd like:
def Concept < ActiveRecord::Base
has_many :child_links, :class_name => 'ConceptLink', :foreign_key => 'parent_id'
has_many :children, :through => :child_links
has_many :parent_links, :class_name => 'ConceptLink', :foreign_key => 'child_id'
has_many :parents, :through => :parent_links
end
def ConceptLink < ActiveRecord::Base
belongs_to :child, :class_name => "Concept"
belongs_to :parent, :class_name => "Concept"
end
I'd also take a look at this blog posting which does a very good of explaining parent-child mappings in rails.

ROR Self referential has_many through with accept_nested_attributes_for

I wondered if someone could take a quick look at this. I'm making a simple conversion application which converts between units of measurement. I need to be able to self reference the table using a join table which stores the relationship between each, along with the conversion between each. This then would be referenced between either side of the relationship. For example 1cm = 10mm and 10mm = 1cm.
So thus far I have this:
#migrations
create_table :measures do |t|
t.string :name
end
create_table :measure_measures do |t|
t.integer :measure_id
t.integer :related_measure_id
t.integer :conversion
end
class Measure < ActiveRecord::Base
has_many :related_measures,
:foreign_key => 'measure_id',
:class_name => 'MeasureMeasure',
:dependent => :destroy
has_many :measures, :through => :related_measures
accepts_nested_attributes_for :related_measures,
:reject_if => proc { |attrs| attrs['related_measure_id'].blank? ||
attrs['quantity'].blank? },
:allow_destroy => true
end
#controller
#measure = Measure.find params[:id
#form
<% form_for #measure do |f| %>
<% fields_for :related_measures do |f_r_m| %>
<%= f_r_m.text_field :related_measure_id -%>
<%= f_r_m.text_field :quantity -%>
<% end %>
<% end %>
For the most part this works ok. Except I cannot access the name of the related measure, only the owner.
I need to get it somehow like this:
f_r_m.object.related_measure.name
but clearly despite my best efforts i cannot set it up and receive the error.
undefined method `owner_measure' for #<MeasureMeasure:0x1053139a8>
Help would be very much appreciated. :)
At first glance the problem comes from the has many through definition. By failing to define the foreign key, Rails, assumes measure_id in the join table. Which would just link back to the measure you're trying to find it from.
has_many :measures, :through => :related_measures, :foreign_key => :related_measure_id
We can't diagnose this error without seeing the innards of the join model.
f_r_m.object.related_measure refers to join table.
But I suspect it's because you haven't defined the relationship properly in the join model. Which should look something like this:
class MeasureMeasures < ActiveRecord::Base
belongs_to :measure
belongs_to :related_measure, :classname => "Measure"
end

Resources