One-to-many and acts_as_tree troubles


#1

ok. so i’ve got two models, Photo and Place:

class Photo < ActiveRecord::Base
belongs_to :place, :counter_cache => true

class Place < ActiveRecord::Base

 acts_as_tree :order => "name"
 has_many :photo

the places database contains specific locations, cities, and states. the
specific locations connect to cities by parent_id and in turn cities
connect to states by parent_id.

each Photo belongs to a Place, referencing to a place_id in the photos
table.

for some reason, i can’t get the one-to-many relationship to work out.
when i put

<%= place.photos.size %>

in a view, i get a NoMethodError saying that ‘photos’ is an undefined
method of the Place model.

any ideas?


#2

On Jul 13, 9:23 am, Zeke R. removed_email_address@domain.invalid
wrote:

the places database contains specific locations, cities, and states. the

in a view, i get a NoMethodError saying that ‘photos’ is an undefined
method of the Place model.

any ideas?


Posted viahttp://www.ruby-forum.com/.

Try to change :
from
class Place < ActiveRecord::Base
acts_as_tree :order => “name”
has_many :photo

to
class Place < ActiveRecord::Base
acts_as_tree :order => “name”
has_many :photos #<------
may be help you
p.s. sorry for my english


#3

actually, the recursive descendants method doesn’t really work. here’s a
replacement.

 def descendants
      descendants = []
      self.children.each do |child|
           descendants << child
           if child.children
                child.children.each do |grandchild|
                     descendants << grandchild
                end
           end
      end
      return descendants.flatten
 end

i’m able to successfully use this function elsewhere, but i still can’t
get the :conditions of the has_many to call include_descendents…

z.


#4

ah, that did it! thanks!!

now i’ve got a new problem. so, with my hierarchical places structure
i’d like to be able to count (and allow to be counter_cache-d) the
number of photos associated with any descendant of a particular place.

for example, say i’ve got a photo that references the place “central
park” which in turn references to “manhattan” which references to “new
york” in the Places tree. i’d like to be able to call this:

 Place.find_by_name("new york").photos.size

and get the number of photos taken anywhere in manhattan and new york
combined… i thought this could be accomplished by adding a :conditions
to the has_many statement in the Place model:

 class Place < ActiveRecord::Base

      acts_as_tree :order => "name"
      has_many :photos, :conditions => include_descendants

      def descendants
           (self.children.each { |child| child.descendants }.flatten 
  • self)
    end

        def include_descendants
             conditions = "place_id = #{self.id}"
             self.descendants.each do |place|
                  conditions << " or place_id = #{place.id}"
             end
             return conditions
        end
    

    end

but i get a NameError, saying that include_descendents is undefined. any
suggestions on how to get this to work?

thanks in advance,
z.


#5
 class Place < ActiveRecord::Base

      acts_as_tree :order => "name"
      has_many :photos, :conditions => include_descendants

      def descendants
           (self.children.each { |child| child.descendants }.flatten 
  • self)
    end

        def include_descendants
             conditions = "place_id = #{self.id}"
             self.descendants.each do |place|
                  conditions << " or place_id = #{place.id}"
             end
             return conditions
        end
    

    end

the problem you have is that when you write

class Klass
has_many :photos, :conditions => include_descendants
end

you’re actually writing
class Klass
self.has_many(:photos, :conditions => include_descendants)
end

ie. the method is being called as a class method on Klass,
and, as such, the “include_descendants” is being evaluated when you
declare the has_many.

so, the following would evaluate like this

class Klass
def self.include_descendants
“this string gets evaluated at class load time”
end
has_many :photos, :conditions => include_descendants
end

this would actually define

has_many :photos, :conditions => “this string gets evaluated at class
load time”

which is plainly not what you want.

(if i wasnt about to go play tennis, I’d have a go at writing the
recursive relation in SQL for you, so we can make it work proper)


#6

i’m able to successfully use this function elsewhere, but i still can’t
get the :conditions of the has_many to call include_descendents…

ack! include_descendants, rather.

z.


#7

has_many :photos, :conditions => “this string gets evaluated at class
load time”

which is plainly not what you want.

ah! i see where this is going now.

(if i wasnt about to go play tennis, I’d have a go at writing the
recursive relation in SQL for you, so we can make it work proper)

eek! maybe i’m going down the wrong road for storing this information.
perhaps i should migrate to acts_as_nested_set or dotted ids (as
suggested at
http://blog.rapidred.com/articles/2006/06/08/ruby-working-with-trees) so
i could find all descendants with one sql query.

maybe then i could set up the has_many :conditions more easily to work
with counter_cache? after all, i could just maintain a photos_count
column of my places table manually, updating it whenever a place is
added to or deleted from the database…

thoughts?

z.