Forum: Ruby on Rails Inheritance in Rails - I need some help

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Adam B. (Guest)
on 2006-04-16 00:07
I've got a model, "category" and another model "subcategory." Each
subcategory belongs_to a category, and a category has_many
subcategories.

What I need to do is set it up so that I can search Category and
Subcategory with one .find call. So:

Category.find(:all, <etc)

will find categories and subcategories.

Now, I believe this can be done by making Subcategory a subclass of
Category. Am I right? Unfortunately, there's almost nothing in AWD about
subclasses and has_many relationships, so I'm lost. I tried to just
change Subcategory to extend Category in the model, but that led to
errors (no such field: "categories.category_id"). What else do I need to
change to allow Subcategory to extend Category and also belong_to a
category?

Thanks,

Adam
Jeff Everett (Guest)
on 2006-04-16 00:49
(Received via mailing list)
Adam,

I would probably do this with a single table that references itself,
rather than have a new model Subcategory extend from Category.
Basically you just need a categories table that contains a parent_id
column referring to another row in that same table. There is a brief
discussion of this at:

http://wiki.rubyonrails.com/rails/pages/HowToCreat...

It is also discussed in AWD ( ~ page 255 ) when they go over "Acts As
Tree".

Hope that helps,

Jeff
Matthew P. (Guest)
on 2006-04-16 00:49
(Received via mailing list)
On Sat, Apr 15, 2006 at 10:07:10PM +0200, Adam B. wrote:
>
> Now, I believe this can be done by making Subcategory a subclass of
> Category. Am I right?

Possibly-sorta, if when you say "subclass" you mean to use single-table
inheritance.  In that instance, yes, you can search Categories and also
get
matching subcategories, because STI is wonderfully intelligent about
that
sort of thing, like so:

CREATE TABLE categories (
  id INTEGER PRIMARY KEY,
  type VARCHAR(255) NOT NULL,
  name VARCHAR(255) NOT NULL,
  category_id INTEGER
);

class Category << ActiveRecord::Base
  has_many :subcategories
end

class SubCategory << Category
  belongs_to :category
end

(All that off the top of my head, so it might not be strictly correct)

Then, when you do a Category.find(), the SQL that will be generated will
include "WHERE type='Category' OR type='SubCategory'", while
SubCategory.find() will only have "WHERE type='SubCategory'".

Speaking structurally, though, I'm not sure that STI really describes
what
you're looking for.  I'd consider putting together my own
Category.find()
type method that also ran the find on the subcategories itself, or else
using something like acts_as_tree to do the whole thing (although I
don't
know if acts_as_tree has the searching capabilities you want -- I've
never
used it myself).

- Matt
Adam B. (Guest)
on 2006-04-16 01:50
Thanks guys!

-Adam
Alain R. (Guest)
on 2006-04-16 02:45
(Received via mailing list)
Adam
    > What I need to do is set it up so that I can search Category and
    > Subcategory with one .find call. So:
    > Category.find(:all, <etc)
    > will find categories and subcategories.


You could aggregate 2 searches in 1 call :

(untested:)

def find_deep (*args)
     result = Category.find(args)
     result << Subcategory.find(args)
     result.flatten
end

Alain
Adam B. (Guest)
on 2006-04-16 03:26
Hmmm. I have a different but related question. Is there such a thing as
an "includes" condition for .find? i.e. if I'm searching for listings
(which habtm categories) that are in a certain category with id =
category_id I could do:

Listing.find(:all, :conditions => [ "categories INCLUDE ?", category_id
]

and, as an extension, is there any way to find a listing that is in a
category with category_id = one of [ id1, id2, id3 ]. That way I could
do something like:

Listing.find(:all, :conditions => ["name = ?, or <is in a category which
is in category_ids>,
                                                                  or <is
in a subcategory which is in subcategory_ids>",
                                                  "%#{params[:search_string]}%",
<category_ids>, <subcategory_ids>]

and find every listing with one search.

It seems like it'd be something I could do with a pure-Ruby .find, but
I'm not sure how hard that would be to integrate.

-Adam
Adam B. (Guest)
on 2006-04-16 03:28
Formatting on that last pseudo-code chunk got really messed up.

Listing.find(:all, :conditions =>
["name = ?,
or <is in a category which is in category_ids>,
or <is in a subcategory which is in subcategory_ids>",
"%#{params[:search_string]}%", <category_ids>, <subcategory_ids>]
Alain R. (Guest)
on 2006-04-16 11:41
(Received via mailing list)
Adam
    > Formatting on that last pseudo-code chunk got really messed up.
    > Listing.find(:all, :conditions => ["name = ?,
    > or <is in a subcategory which is in subcategory_ids>",
    > "%#{params[:search_string]}%", <category_ids>, <subcategory_ids>]


When in doubt, just do it... with the console:

    ./script/console
    >> Foo.find_by_sql('select * from foes where id in (1,2,3,4,5)')


(On Windows, you launch the console with
       ruby script\console
)

Alain
Adam B. (Guest)
on 2006-04-16 23:09
Thanks. I did end up solving the problem with a few extra steps, but
I'll remember that SQL query in the future. :)

-Adam
This topic is locked and can not be replied to.