Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails "where" clause for associations

This seems like a simple question but it's a little puzzle to me:

class Parent
  has_many children
  ...
end

class Child
  belongs_to parent
end

p = Parent.find(111)
c = Child.all.where(parent: p)

Why doesn't that work, and how come I have to do:

c = Child.all.where(parent_id: p.id)

Addendum

A more complicated case has me creating a Relation based on more complicated logic, e.g.

c = Child.where(age: 32, city: "boston")
c.where(parent: p) # wouldn't work

Addendum #2

Wait I need to have a many to many to illustrate this:

class Teacher
   has_many :students, through: ClassRoom
   has_many :classes
end

class ClassRoom
  belongs_to :teacher
  belongs_to :child
end

class Child 
  has_many :classes
  has_many :teachers, through: ClassRoom
end
t = Teacher.first
c = Child.where(age: 5, city: "boston")

c.where(teacher: t) # wouldn't work
c.where(teacher_id: t.id) # would work but is a little ugly

Addendum 3

Thanks for all the great info! Is there a better (or 'correct') way to do the last line form the above example?

c.where(teacher_id: t.id) # would work but is a little ugly
like image 944
pitosalas Avatar asked Feb 17 '23 16:02

pitosalas


2 Answers

You can do:

p = Parent.find(111)
all_children = p.children

The key parent doesn't work because it using that as the column name.

Addendum:

So for this use case you should use:

class ClassRoom < ActiveRecord::Base
  belongs_to :teacher
  belongs_to :child
end

class Teacher < ActiveRecord::Base
  has_many :children, through: ClassRoom
  has_many :class_rooms
end

class Child < ActiveRecord::Base
  has_many :class_rooms
  has_many :teachers, through: ClassRoom
end

t = Teacher.first
teachers_children_from_boston_and_32 = t.children.where(age: 32, city: "boston")

Firstly you can't use Class because it is an object already. The next problem was that you renamed children to students, which you can do but then need to do some other options on the has_many call.

Check out joining tables here: http://guides.rubyonrails.org/active_record_querying.html#joining-tables

And Assoications here (your use case matches this example perfectly): http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association

Also remember with rails 3 all where clauses are just critera. Critera is used to find your matches and can be added together to narrow your results. IE

where_clause_one = Teacher.where(age: 50)
where_clause_two = Teacher.where(city: "San Francisco")
merged_where_clauses = where_clause_one.merge(where_clause_two)
merged_where_clauses.each do |teacher|
  # teachers that are both 50 and from san francisco
  ...
end
like image 91
earlonrails Avatar answered Feb 19 '23 06:02

earlonrails


.all converts an ActiveRecord::Relation object to an array. Arrays do not respond to the where method. You should use

c = Child.where(parent_id: p.id).all

You have to use _id in this case because where will directly translate the given hash into SQL. SQL does not know what parent is, it only knows what parent_id is. That being said, the best way to do this would be

c = p.children
like image 24
Logan Serman Avatar answered Feb 19 '23 06:02

Logan Serman