简体   繁体   中英

How can I get some model's objects and its associated model's objects conditionally through an intermediary model object, using ActiveRecord

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 jobs 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM