views:

123

answers:

2

I'm looking for a clean way to filter the children of a parent in a has_many relationship without hitting the database and thus reloading the db's view of the objects back into the app.

For example:

class Parent < ActiveRecord::Base
  has_many :children, ...
  ...
end

My understanding (correct me if I'm wrong) is that

parent.children.find_all_by_attr('foo')

returns all the parent's children that have a attr value of 'foo' in the db but because it hits the database again, any children that have had their attr values set to foo before being saved will have their db values restored thus overwriting any changes.

I've hacked around this with

parent.children.reject { |child| child.attr != 'foo' }

but this seems very sloppy and more difficult to read. Does anybody have a cleaner suggestion on how to do this?

A: 

You could do the opposite of reject, which is find_all:

parent.children.find_all {|child| child.attr == 'foo' }
dvyjones
+2  A: 

After doing some poking around, it looks like it is a little more complicated than that. My poking around went like this:

  • I created a pair of models, Parent and Child with a has_many relationship in a throwaway rails app.
  • I opened up script/console and poked around.

I created a new parent with a child and saved them

>> p = Parent.new;p.children << Child.new(:foo=>'bar');p.save
=> true

See the child is in the db and findable by_foo

>> p.children.find_by_foo('bar')
=> #<Child id: 1, foo: "bar", parent_id: 1, created_at: "2009-12-14 22:08:05", updated_at: "2009-12-14 22:08:05">

I added another child to the collection,it shows up in p.children but not in collection methods that hit the db.

>> p.children << Child.new(:foo=>'bar')
=> [#<Child id: 1, foo: "bar", parent_id: 1, created_at: "2009-12-14 22:08:05", updated_at: "2009-12-14 22:08:05">, #<Child id: 2, foo: "bar", parent_id: 1, created_at: "2009-12-14 22:08:25", updated_at: "2009-12-14 22:08:25">]
>> p.children.find_by_foo('bar')
=> #<Child id: 1, foo: "bar", parent_id: 1, created_at: "2009-12-14 22:08:05", updated_at: "2009-12-14 22:08:05">

I change the child that is in the db.

>> p.children[0].foo = 'baz'
=> "baz"

When I search for it, it gives me the db version.

>> p.children.find_by_foo('bar')
=> #<Child id: 1, foo: "bar", parent_id: 1, created_at: "2009-12-14 22:08:05", updated_at: "2009-12-14 22:08:05">

But, the local collection is unchanged.

>> p.children
=> [#<Child id: 1, foo: "baz", parent_id: 1, created_at: "2009-12-14 22:08:05", updated_at: "2009-12-14 22:08:05">, #<Child id: 2, foo: "bar", parent_id: 1, created_at: "2009-12-14 22:08:25", updated_at: "2009-12-14 22:08:25">]

So if you save p again, it will pass on the changes.

If you want to get all the local association objects, including ones that have been changed, you can't use the ActiveRecord finders because they hit the db, instead use array methods like you did above. Though, using find_all or select would be easier to understand

parent.children.select{|c| c.attr == 'foo'}
BaroqueBobcat