ActiveRecord::Relation issue

Hi, everyone.

Yesterday found an interesting issue in scope method.
Let`s say, you have next model structure:

Model A --has_many-> Model B --has_many-> Model C --has_many-> Model_D

Model D has some attribute, let`s say, 'status' ENUM('opened',
'closed').

In Model A I would like to take all records, where Model D`s status is
opened.
This is a little bit confusing.

Here is an example.
We have a project. A project has many tasklists. Tasklist has many
tasks.
I would like take all projects, which have one or more tasks opened.
I hope, there should be any possibility to call in controller just
active_projects = Project.all.active.

I would appreciate any help.

Max Reznichenko

I guess I have to chalk to up to another "I didn't know you could do
that". I'll also admit I have no idea how efficient this is.

I've used a technique where I query a belongs_to table with the ids of
a has_many related table. In my test I have
  Assessments has_many -> Questions has_many -> Answers

I'd do something like
  answer_ids = Answer.where(:requires_other =>
true).select(:question_id).map(&:question_id)
  # @foo.map(&:id) is shorthand for @foo.map{|i| i.id}, which gets
array of ids
  question_ids = Question.where(:id =>
answer_ids).select(:assessment_id).map(&:assessment_id)
  @assessments = Assessment.where(:id => question_ids)

Or, if you want one big hard to read statement!

@assessments = Assessment.where(:id => Question.where(:id =>
Answer.where(:requires_other =>
true).select(:question_id).map(&:question_id)).select(:assessment_id).map(&:assessment_id))

Someone else will have to chime in on how efficient or inefficient
this is. My last project was on a database that was very reliant on
"sets" and this is the closest thing I've found in rails to that
concept.

Steve

Someone else will have to chime in on how efficient or inefficient
this is. My last project was on a database that was very reliant on
"sets" and this is the closest thing I've found in rails to that
concept.

Just To clarify the last statement, the Database environment was 4D
and in 4D you'd do

QUERY([Answers];[Answers]requires_other = true)
RELATE ONE SELECTION([Answers];[Questions])
RELATE ONE SELECTION([Questions];[Assessments])

Don't know if there is an equivalent approach in Rails.

Steve

Hi, everyone.

Yesterday found an interesting issue in scope method.
Let`s say, you have next model structure:

Model A --has_many-> Model B --has_many-> Model C --has_many-> Model_D

Are the reverse relationships has_many or belongs_to?

Colin

Colin Law wrote in post #954645:

Are the reverse relationships has_many or belongs_to?

Colin

Yes, sure.
A <--belongs_to- B <--belongs_to- C <--belongs_to- D

2Steve.

Here you have a right solution, but this code should be written in
controller. In this case, I could easily run through all records and
calculate the Model A`s state.

Anyway, for 3 model structure there is actually a solution. Here it is:
#Model C definition
belongs_to :model_b

#Model B definition
has_many :model_c
belongs_to :model_a

#Model A definition
has_many :model_b
-->has_many :model_c, :through => :model_b

Max Reznichenko

Colin Law wrote in post #954645:

Are the reverse relationships has_many or belongs_to?

Colin

Yes, sure.
A <--belongs_to- B <--belongs_to- C <--belongs_to- D

2Steve.

Here you have a right solution, but this code should be written in
controller. In this case, I could easily run through all records and
calculate the Model A`s state.

Anyway, for 3 model structure there is actually a solution. Here it is:
#Model C definition
belongs_to :model_b

#Model B definition
has_many :model_c
belongs_to :model_a

#Model A definition
has_many :model_b
-->has_many :model_c, :through => :model_b

I have not tried it but can you then say
has_many :model_d, :through -> :model_c

If you can then I think you can just find A where a.d.state is whatever.

Colin

At the end of the day, the most important thing is that it works. However, I think we should try to get the database do the work. I'll apologize in advance that this example is rails2.

# Project -> TaskList -> Task
Project.all(:group => "projects.id", :joins => {:task_lists => :task}, :conditions => "tasks.status = 'open'")

This results is a list of projects that have open tasks.

Luke

Luke Cowell wrote in post #954803:

At the end of the day, the most important thing is that it works.
However, I think we should try to get the database do the work. I'll
apologize in advance that this example is rails2.

# Project -> TaskList -> Task
Project.all(:group => "projects.id", :joins => {:task_lists => :task},
:conditions => "tasks.status = 'open'")

This results is a list of projects that have open tasks.

Luke

Thanks a lot, Luke.
This is exactly, what I meant.

Actually, there is next structure:

User -> Project -> Tasklist -> Task

Thanks to Luke, I wrote next scope in the model:

class Project < ActiveRecord::Base
  has_and_belongs_to_many :users
  has_many :tasklists
  belongs_to :user

  def self.active
    all(:group => 'projects.id', :joins => {:tasklists => :tasks},
:conditions => "tasks.status = 'open'")
  end
end

And now the call User.first.projects.active returns all the projects
with active tasks.

Max Reznichenko

Colin Law wrote in post #954800:

belongs_to :model_a

#Model A definition
has_many :model_b
-->has_many :model_c, :through => :model_b

I have not tried it but can you then say
has_many :model_d, :through -> :model_c

If you can then I think you can just find A where a.d.state is whatever.

Why not use the nested_has_many_through plugin?

Colin

Best,

At the end of the day, the most important thing is that it works. However, I think we should try to get the database do the work. I'll apologize in advance that this example is rails2.

# Project -> TaskList -> Task
Project.all(:group => "projects.id", :joins => {:task_lists => :task}, :conditions => "tasks.status = 'open'")

This results is a list of projects that have open tasks.

Luke

Still not a lot out there on rails3 finders/arel examples. Is this the
rails3 version of the above?

Project.group(:id).joins(:task_lists => :tasks).where(:tasks =>
{:status => 'open'})

Seemed to work in my assessments example

Assessment.group(:id).joins(:questions => :answers).where(:answers =>
{:requires_other => true})