This has come up again and again in the project I'm working on so am going to try and get an answer once and for all.
I'm trying to use where, and then collect all of an association. I can't describe the problem well so it may have been answered before (in fact, I'm sure it has) but here is an example.
I have applicant_approvals, which is a join table between a few different relations. It also has its own column, approved. I'm trying to get all applicant approvals where an applicant has been approved, and then get those applicants. So something like:
#stage.applicant_approvals
.where(approved: true)
.applicants
Now, of course, you can't do that because where returns an active record association. I don't want to use first because I want all the applicants that have been approved of that stage of the process.
Is there an acknowledged way to do this or do I need to get the association, then run it through an each to get each applicant?
thanks.
You can do this by setting up indirect associations:
class Applicant < ApplicationRecord
has_many :applicant_approvals
has_many :stages, through: :applicant_approvals
end
class ApplicantApproval < ApplicationRecord
belongs_to :applicant
belongs_to :stage
end
class Stage < ApplicationRecord
has_many :applicant_approvals
has_many :applicants, through: :applicant_approvals
end
Then just create pop a where clause onto the association:
#stage.applicants.where(applicant_approvals: { approved: true })
Related
I'm creating an app that has a User and a Plugin model. A user can have multiple plugins and a plugin can belong to multiple users, which I've implemented using a junction table.
class Plugin < ActiveRecord::Base
has_many :user_plugins
has_many :users, through: :user_plugins
end
class User < ActiveRecord::Base
has_many :user_plugins
has_many :plugins, through: :user_plugins
end
class UserPlugins < ActiveRecord::Base
belongs_to :user
belongs_to :plugin
end
However, I then want to store arbitrary data for each user plugin (for example, things like api keys, options etc that can differ for each plugin.).
My initial approach was to have a user_plugins_options that joined on user_plugins, but I can't seem to get this to work correctly.
class UserPluginOptions < ActiveRecord::Base
belongs_to :user_plugins
end
My question, how should I go about approaching this to best work with ActiveRecord?
I think you misnamed your class, as the table is user_plugins but the model is UserPlugin. It’s plausible you are running into issues because of this.
Agree with Alex. Why don’t you create a json field on UserPlugin called options and keep a hash of plugin specific values here.
If you must have another table, you should add a has_one :user_plugin_option to your UserPlugin
Below given are the model in my application. I am trying to find all the companies that matches the keyskills. For example. If I type java in my search-box it should get me all the companies that are matching and keyskills.
class User < ActiveRecord::Base
has_one :company
end
class Company < ActiveRecord::Base
has_many :jobs
belongs_to :user
end
class Job < ActiveRecord::Base
belongs_to :company
has_many :key_skills, dependent: :destroy
accepts_nested_attributes_for :key_skills, reject_if: :all_blank, allow_destroy: true
end
class KeySkill < ActiveRecord::Base
belongs_to :job
end
The steps that I am following are,
step1: Find all the keyskills matching the entered word. (ex: java)
#matched_keyskills = KeySkill.where('name like ?','java')
Since I have association between jobs and keyskills, that is jobs has_many key_skills and key_skills belongs_to job. I can iterate over
#matched_keyskill.each do |k|
k.job.company
end
and get the company records. But, when I tried this method it results in n+1 query also the company name is getting repeated.
Is there a way through which I can get only the company name shown on show page then by clicking on company it shows the jobs associate to it.
also kindly let me know is the db model and association are correct inorder to achieve it.
You can use "joins" and "inclue" to remove the n+1 query:
Try this, it will give you the list of all companies as You required.
Company.joins(key_skill: :job).where('key_skill.name like ?','java')
you can also use eager loading.
http://railscasts.com/episodes/22-eager-loading
I currently have two Rails models as follows:
class Job < ApplicationRecord
has_many :job_interests
end
class JobInterest < ApplicationRecord
belongs_to :job
belongs_to :applicant
enum status: { pending: 0, accepted: 1, rejected: 2, cancelled: 3 }
end
I am trying to achieve that for a specific applicant (user) I can serve a list of Jobs. Each Job should be associated with one or zero JobInterest instances (for that specific user).
I've thought of multiple ways to achieve this, but all seem a bit cumbersome.
Possible solution 1:
Basically use a LEFT OUTER JOIN, e.g.
Job.includes(:job_interests).where(job_interests: { applicant_id: [current_applicant.id, nil] }).all
This results in a job list, each job having a property called job_interests which should have one item or none. One if the applicant has expressed interested, and none if the applicant hasn't done so.
Drawback: this doesn't seem like to the most straightforward approach.
Possible solution 2:
When a Job is created, automatically create JobInterests for all applicants belonging to this job. Given a lot of applications, this doesn't scale well and requires a lot of extra rows.
On the other hand, one can now get the Job list by calling
JobInterest.where(applicant_id: current_applicant.id).all
, which seems much cleaner than the previous approach.
Would you pick any of these approaches, or suggest another approach I haven't thought of.
So based on my comments in your question I think its just a simple has_many :through association. A User has many job interests and a Job has many interested Users.
class JobInterest < ApplicationRecord
belongs_to :user
belongs_to :Job
end
class User < ApplicationRecord
has_many :jobinterests, dependent: :destroy
has_many :jobs, :through => :jobinterests
end
class Job < ApplicationRecord
has_many :jobinterests, dependent: :destroy
has_many :users, :through => :jobinterests
end
This way you can simply add jobs to job interests when you view them, and remove them from the list when the user opts out of the job.
I am trying to create an association between two tables. A student table and a computer table.
A computer can only ever be assigned to one student (at any one time) but a student can be assigned to multiple computers.
This is what I currently have in mind. Setting up a has-many through relationship and modifying it a bit.
class Student < ActiveRecord::Base
has_many :assignemnts
has_many :computers, :through => :assignments
end
class Computer < ActiveRecord::Base
has_one :assignment
has_one :student, :through => :assignments
end
class Assignment < ActiveRecord::Base
belongs_to :student
belongs_to :computer
end
Does this seem like the best way to handle this problem? Or something better sound out quickly to the experts here. Thanks!
You need first to decide if a simple one-to many relationship is enough for you.
If yes, it gets a lot easier, because you can get rid of the Assignment-class and table.
Your database-table "computers" then needs a student_id column, with a non-unique index
Your models should look like this:
class Computer < ActiveRecord::Base
belongs_to :student
end
class Student < ActiveRecord::Base
has_many :computers, :dependent => :nullify
end
"dependent nullify" because you don't want to delete a computer when a student is deleted, but instead mark it as free.
Each of your computers can only be assigned to a single student, but you can reassign it to a different student, for example in the next year.
Actually your approach is fine, as one offered by #alexkv. It is more discussion, than question.
Another thing if you want to use mapping table for some other purposes, like storing additional fields - then your approach is the best thing. In has_many :through table for the join model has a primary key and can contain attributes just like any other model.
From api.rubyonrails.org:
Choosing which way to build a many-to-many relationship is not always
simple. If you need to work with the relationship model as its own
entity, use has_many :through. Use has_and_belongs_to_many when
working with legacy schemas or when you never work directly with the
relationship itself.
I can advise you read this, to understand what approach better to choose in your situation:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
http://blog.hasmanythrough.com/2006/4/20/many-to-many-dance-off
You can also use has_and_belongs_to_many method. In your case it will be:
class Student < ActiveRecord::Base
has_many :assignemnts
has_and_belongs_to_many :computers, :join_table => 'assignments',
end
class Computer < ActiveRecord::Base
has_one :assignment
has_and_belongs_to_many :student, :join_table => 'assignments',
end
or you can rename assignments table to computers_students and remove join_table
class Student < ActiveRecord::Base
has_many :assignemnts
has_and_belongs_to_many :computers
end
class Computer < ActiveRecord::Base
has_one :assignment
has_and_belongs_to_many :student
end
I have three Models setup with the following associations
class User < ActiveRecord::Base
has_many :faculties
has_many :schools, :through => :faculties
end
class School < ActiveRecord::Base
has_many :faculties
has_many :users, :through => :faculties
end
class Faculty < ActiveRecord::Base
belongs_to :user
belongs_to :school
end
and in my controller i go to create a school and assign the user
class SchoolsController < ApplicationController
def create
#school = current_user.schools.build(params[:school])
...
end
end
When I login and submit the form the flash displays success, but the association doesn't build on the join table.
I tried it inside the apps console and it builds the association just fine.
I've been stuck on this for a couple days now and I just cannot figure out what I am missing. Thank in advance for any and all advice
The build method does not save the object. You need to explicitly call #school.save.
Two things: If the schools association is :through a has_many association, you will have to select which parent the School exists through.
So, for instance, if you were to nest School resources under users as in /users/:id/faculties/:id you could create a school via current_user.faculties.find(params[:faculty_id]).schools.build(params[:school]).save
Based on the example code, it looks like the fundamental problem is that the has_many xxx, :through syntax is being used without specifying the id of the faculties record. Remember two things: 1) ActiveRecord doesn't natively support composite primary keys, and 2) you must call #save on associated records created using #build. If you remember these, you should be fine.