Ruby on Rails Model association - sql

I am trying to make an application using Rails 3.2.14, but I can't wrap my head around the model associations that I have built so far. I have four models which are interconnected to give desired result but so far it hasn't been working.
Job with fields: user_id, title
Jobapplication with fields: job_id, user_id, isrefused
Resume with fields: user_id, profession
I am trying to extract all the jobs which a particular user has applied for using the jobapplication model in an instance variable for the view.
All tables containing foreign keys have belong_to associations along with has_many at the other end.
So far, I have tried like this in the controller:
def applied job
#jobapplications = Jobapplication.where('user_id = ?', current_user.id)
#jobs = #jobapplications.jobs
end
The purpose is to find jobs for which the user has put in application for.
Should I redesign the models association?

The accessors can be greatly simplified if you write your model associations like this:
class User < ActiveRecord::Base
has_many :jobs # jobs posted
has_many :job_applications # job applications posted
has_many :applied_jobs, through => :job_applications, :source => :job # jobs applied for
has_one :resume
end
class Job < ActiveRecord::Base
belongs_to :user
has_many :job_applications
has_many :applicants, :through => :job_applications, :source => :user # applicants for this job
has_many :applicant_resumes, :through => :job_applications, :source => :resume
end
class JobApplication < ActiveRecord::Base
belongs_to :user
belongs_to :job
has_one :resume, :through => :user # the resume for the application
end
class Resume < ActiveRecord::Base
belongs_to :user
end
Now you can easily find the jobs a user applied for:
current_user.applied_jobs
Or all the applicants (users applying) for a specific job:
#job.applicants
And you can see a user's resume:
current_user.resume
Or an application's resume:
#job_application.resume
Or all resumes for those applying for a specific job:
#job.applicant_resumes

This looks okay:
#jobapplications = Jobapplication.where("user_id =?", current_user.id)
but not sure about this:
#jobs = #jobapplications.jobs
What's the jobs method?
try this:
#some_controller.rb
def applied_job #note the underscore!
#jobapplications = Jobapplication.where("user_id =?", current_user.id)
end
and in the view
<% #jobapplications.each do |application| %>
#list applications here
<% end %>

Related

has_many :through causes error: HasManyThroughSourceAssociationMacroError

I need to assign students (users) to classes (studentclasses)
I have a simple many-to-many relationship using 3 tables:
studentclasses (lesson details)
users (user... student info)
studentclass_users (Join table containing user_id and studentclass_id)
I'm using has_many :through and my models look like the following:
studentclass.rb
class Studentclass < ActiveRecord::Base
has_many :studentclass_users
has_many :users, :through => :studentclass_users
end
user.rb
class User < ActiveRecord::Base
...more here...
has_many :studentclass_users
has_many :studentclasses, :through => :studentclass_users
end
studentclass_users.rb
class StudentclassUsers < ActiveRecord::Base
belongs_to :studentclass
belongs_to :user
end
For testing purposes, I'm just using a hidden field in my studentclass new view partial to tack on a user id when creating the class and it is as follows:
_new.html.erb
<%= hidden_field_tag "studentclass[user_ids][]", 29%>
And in my studentclass controller:
studentclasses_controller.rb
def new
#studentclass = Studentclass.new
end
def create
#studentclass = Studentclass.new(params[:studentclass])
#studentclass.save!
end
My params comes back:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"MjBTf4rtcyo8inADrSxPZB3vLOKtlZRVFlQJJzfCqWs=", "studentclass"=>{"class_title"=>"hjhkj", "user_ids"=>["29"]}, "commit"=>"Save"}
which seems fine but I get the following error:
NameError (uninitialized constant ActiveRecord::HasManyThroughSourceAssociationMacroError):
I think this is something simple with naming maybe? Any help would be appreciated

What's the rails way to include a field in a join model when listing an association?

So if I have the following relationship
class Item < ActiveRecord::Base
has_many :item_user_relationships
has_many :users, :through => :item_user_relationships
end
class User < ActiveRecord::Base
has_many :item_user_relationships
has_many :items, :through => :item_user_relationships
end
class ItemUserRelationship < ActiveRecord::Base
belongs_to :item
belongs_to :user
attr_accessible :role
end
What's the rails way to include the role attribute when listing all the Users of an Item?
#users = #item.users # I want to include the role as part of a user
Thanks!
UPDATE: I'm still having trouble with this. My goal is to get an array of User models that have their role included in the attributes.
I'm note sure if I understand you correctly, but maybe this is what you want?
#users = #item.users
#users.each do |user|
puts user.item_user_relationships.first.role
end

Rails calling User record from Friends model

I've built a simple Friend model, which allows Users to have multiple friends. Here's what that looks like:
class Friend < ActiveRecord::Base
belongs_to :user
class User < ActiveRecord::Base
has_many :friends
Each friend record just has an id, user_id and friend_id. The user_id is the id of the user it belongs to and the friend_id is the id of user they are befriending.
Here's my problem
I'm not quite sure how to display a list of a particular user's friends. #user.friends will give me a list of all the friend records they have, but not the user accounts of those friends.
For instance, I am trying to build a show page for the friends controller:
class FriendsController < ApplicationController
def show
#user = current_user
end
SHOW.HTML.ERB
<% if #user.friends.count > 0 %>
<% #user.friends.each do |friend| %>
<div class="entry">
<%= friend.username %>
This does not work because friend in this case does not have username. I need to do something like this in my controller:
#friend = User.find_by_id(friend.friend_id)
But I'm not sure how I would call that in my view in the #user.friends loop. Any thoughts appreciated. Let me know if I need to be more clear.
UPDATE
I've updated my User model like so:
has_many :friends, :include => :user
has_many :friended_users, :through => :friends, :source => :user, :uniq => true
However, when I run #user.friended_users it's giving me the user_ids (which is the same as #user) rather than friend_ids.
How can I tweak that relationship so it's linking to the friend_id rather than user_id?
The more I think about it, I think I may not have set up the relationship properly in the first place. Maybe a User should has_many :users, through => 'friends', but that doesn't really make sense...
UPDATE
I've updated my models based on #twooface's input:
class User < ActiveRecord::Base
has_many :friendships
has_many :friends, :through => :friendships
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => 'User'
class Friend < ActiveRecord::Base
has_many :friendships
has_many :users
I'm just not sure what my Friends table should look like. I assume it should have a primary key and a user_id? If I create a friendship and friend record, I can do friendship.user and friendship.friend and get the correct results, but user.friends gives me an empty hash...
I think your relations are built a bit wrong. Try something like this:
class User < ActiveRecord::Base
has_many :friends
has_many :friendships
has_many :friends, :through => :friendships
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => 'User'
# This class has :user_id and :friend_id
Then everything will be simpler. Every user will have an array of friends that will be just users.
User.first.friends
Will return an array of Users that this User friends.
Hope this helps.

How do i create an object if it has more than one belongs_to?

I have the following:
class Org < ActiveRecord::Base
has_many :users
has_many :entries
end
class Entry < ActiveRecord::Base
belongs_to :org
belongs_to :user
validates_presence_of :entry_text
end
class User < ActiveRecord::Base
belongs_to :org
has_many :entries
validates_uniqueness_of :user_name
validates_presence_of :user_name, :length => { :minimum => 3 }
end
I can Create Orgs and Users... How do i create an entry if there are two belongs_to? and what is this pattern called?
Double nested resources are tricky. The trick with users usually is to keep it out of your desired entry path.
Your question is kind of broad, but if you specify more information, people would be able to help you better. Also, I would recommend using the gem Devise for your user management system. Since you're using 'users' I would assume you want users from orgs to create entries. The entry created would be a part of org and the user would be the session's current user. Sorry if I am wrong to assume this.
Your routes.rb file can look something like this (assuming rails 3):
resources :orgs do
resources :entries
end
Then the create of your entry controller would look like:
#entry = #org.entries.new(params[:topic])
#entry.user = current_user #or however you are managing the current user's session.
And you'd want to set the org for the entire class by making a method that loads your current org and do a before_filter :loadOrg
def loadOrg
#org = Org.find(params[:id])
end
This is of course assuming your path is something like: /org/(id)/entry/(entry_id)
and not
/org/(id)/user/(user_id)/entry/(entry_id)
which in my opinion is unnecessary and can lead to more problems. You can always create a userpage model that calls all entries by users, but the default route doesn't necessarily have to include users in the path.
I don't see any problem.
#entry = Entry.create(:entry_text => "Hello World!")
Now questions to clarify what do you need:
Can #entry belongs both org and user at the same time? Or it can belongs to only one of them?
Should #entry belongs to at least one of them?
If #entry supposed to belong only one of them, so you should use Polymorphism
http://railscasts.com/episodes/154-polymorphic-association
class Entry < ActiveRecord::Base
belongs_to :textable, :polymorphic => true
validates_presence_of :entry_text
end
class Org < ActiveRecord::Base
has_many :users
has_many :entries, :as => :textable
end
class User < ActiveRecord::Base
belongs_to :org
has_many :entries, :as => :textable
validates_uniqueness_of :user_name
validates_presence_of :user_name, :length => { :minimum => 3 }
end

Avoiding individual database calls for count

My models look like this:
class Movie < ActiveRecord::Base
attr_accessible :title, :year, :rotten_id, :audience_score,
:critics_score, :runtime, :synopsis, :link, :image
has_many :jobs, :dependent => :destroy
has_many :actors, :through => :jobs
end
class Actor < ActiveRecord::Base
attr_accessible :name
has_many :movies, :through => :jobs
has_many :jobs, :dependent => :destroy
end
class Job < ActiveRecord::Base
attr_accessible :movie_id, :actor_id
belongs_to :movie
belongs_to :actor
end
When I'm displaying my index of Actors, I'd like to show the number of movies each actor has starred in. I can do this with #actor.movies.count, however this generates an SQL query for each actor. With, say, 30 actors, this will result in 30 extra queries in addition to the initial.
Is there any way to include the count of movies each actor has participated in, in the initial Actor.all call? And thereby getting things done with only one call. Extra bonus if this was sorted by said count.
Update:
All answers provided has been helpful, and though it turned into some dirt-slinging-contest at some point, it worked out well. I did a mish-mash of all your suggestions. I added a movies_counter column to my Actor model. In my Job model I added belongs_to :actor, :counter_cache => :movies_counter. This works brilliantly, and is automatically updated when i create or destroy a movie, without me adding any further code.
As #Sam noticed, you should add new column to actors table movies_counter
rails g migration add_movies_counter_to_actor movies_counter:integer
Now you can edit your migration
class AddMoviesCounterToActor < ActiveRecord::Migration
def self.up
add_column :actors, :movies_counter, :integer, :default => 0
Actor.reset_column_information
Actor.all.each do |a|
a.update_attribute :movies_counter, a.movies.count
end
end
def self.down
remove_column :actors, :movies_counter
end
end
And run it
rake db:migrate
Then you should add two callbacks: after_save and after_destroy
class Movie < ActiveRecord::Base
attr_accessible :title, :year, :rotten_id, :audience_score,
:critics_score, :runtime, :synopsis, :link, :image
has_many :jobs, :dependent => :destroy
has_many :actors, :through => :jobs
after_save :update_movie_counter
after_destroy :update_movie_counter
private
def update_movie_counter
self.actors.each do |actor|
actor.update_attribute(:movie_count, actor.movies.count)
end
end
end
Then you can call some_actor.movies_counter
Add a column to your Actor table called 'movie_count'. Then add a call back in your Actor model that updates that column.
class Movie < ActiveRecord::Base
has_many :actors, :through => :jobs
before_save :update_movie_count
def update_movie_count
self.actor.update_attribute(:movie_count, self.movies.size)
end
end
That way your just have an integer that gets updated instead of calling all records.