How can I get some model's objects and its associated model's objects conditionally through an intermediary model object? This will then be used to generate JSON (via to_json
).
Here is the setup made more generic:
class JobSet < ActiveRecord::Base
belongs_to :job_system
has_many :resources, through => :job_system
has_many :jobs
...
end
class JobSystem < ActiveRecord::Base
has_many :job_sets
has_many :resources
...
end
class Resource < ActiveRecord::Base
belongs_to :job_system
has_many_and_belongs_to_many :jobs
...
end
class Job < ActiveRecord::Base
belongs_to :job_system
belongs_to :job_set
has_many_and_belongs_to_many :resources
...
end
What I want to do is get all of the resources with their corresponding jobs, but only the jobs that are part of a particular job set.
UPDATE: even if a resource doesn't have any job开发者_JS百科s associated with it I still want to get it with the rest of the resources. The purpose is to display all resources with any jobs assigned to them.
It looks like I can set conditions
when using through
, but I can't seem to figure out how to do that when it is the next objects associations I am after...
Is there a good name for this type of problem? Is there any easy way to do this?
UPDATE 2: There seem to be solutions that work well within the controller, but I am using to_json in the view to preload for a canvas element. It seems to automatically include all jobs when I do this:
var resources = <%= @resources.to_json(:include => {:jobs => {:only => :id}}) %>;
Would a manual SQL JOIN even help with this?
I think I know what you are after. The only thing I would ask you for is, if a resource does not have any jobs in this particular job set, should it still be included?
Anyway, here is one way to do it:
@resources = Resource.all(:include => :jobs,
:conditions => ["jobs.job_set_id = ?", @job_set.id])
This will NOT include the resources that has no jobs associated with @job_set
So, am I totally wrong in my assumptions, or was this what you where after?
Edit
Okay, this was more difficult then I thought. There are several solutions but none I really like. You could do like this:
@resources = Resource.all
@resources.each do |resource|
resource.jobs.all(:conditions => ["jobs.job_set_id = ?", @job_set_id])
end
But that will result in the 1+n number-of-queries-problem putting pressure on your db.
You could also do like this:
@resources = Resource.all(:include => :jobs)
@resources.each do |resource|
resource.jobs.select{ |job| job.job_set_id == @job_set_id }
end
But that will result in more memory consumption on the server side since all jobs are loaded eagerly and then looped through to see which matches the job_set_id.
The problem with finding a better solution in my opinion is that this is something that should be specified in the SQL JOIN condition but I don't think that ActiveRecord lets us modify that in combination with eager loading.
You should be able to do this in a pretty straight-forward way, but you may need the set in a particular format or structure other than what's given here:
@resources = @job_set.resources.include(:jobs)
As a note, I'd advise against using the Rails 1 style has_many_and_belongs_to_many
declarations. The join tables they use are not model based and are very hard to manipulate, especially if they have meta-data associated with them, compared to a more modern join model approach. You simply make a model called JobResource or ResourceJob depending on how you want to prioritize things, and build in two belongs_to
relationships to join them together.
精彩评论