1

I have a model Item, which has a relation to itself.

class Item < ActiveRecord::Base
  has_many :subitems, :class_name => "Item", :foreign_key => "superitem_id"
  belongs_to :superitem, :class_name => "Item"
end

And I want to query all items which have a parent. Firstly I've tried to check if parent_id is present Item.where("superitem_id != ?", false), or something like this. But it doesn't work. Although that item has superitem_id, superitem can be already destroyed. So I have to do it with class method

def self.with_superitems
  items = []
  self.find_each do |i|
    items << i if i.superitem
  end
  return items
end

But it makes chaining impossible, and I want to chain it with similar methods, like

def self.can_be_stored
  items = []
  self.find_each do |i|
    items << i if i.can_be_stored?
  end
  return items
end

Is it possible to achieve the same results with scopes? Or what would you do?

fetsh
  • 1,949
  • 1
  • 16
  • 17

3 Answers3

1

I've had a similar issue in the past. It's sometimes difficult to get round it. I found a hack-ish way of doing it for my purposes so hope this will help...

 ids = []
 self.find_each do |i|
    ids << i.id if i.superitem
 end
Model.where('id in (?)', ids)
Yule
  • 9,668
  • 3
  • 51
  • 72
  • Thank's! Seems like it works =) By the way, i don't understand it good enough yet, but your `items.inject([]){|a,b| a+=[b.id]}` can be replaced by `items.map(&:id)` – fetsh Jun 03 '11 at 11:44
  • Thanks, I've tidied it up a bit anyway so it doesn't need to loop through the list twice – Yule Jun 03 '11 at 12:53
  • Nope. It doesn't chain. `Item.countable.occupied # []`, `Item.countable.occupied.to_sql # "SELECT \"items\".* FROM \"items\" WHERE (id in (10,40)) AND (id in (NULL)) ORDER BY name asc"`. And it hoped it would be like `(Item.countable & Item.occupied) # [#]`, `(Item.countable & Item.occupied).to_sql # "SELECT \"items\".* FROM \"items\" WHERE (id in (10,40)) AND (id in (18,40,45)) ORDER BY name asc"` – fetsh Jun 03 '11 at 13:10
1

In rails 2 i would have done this

items = Item.find(:all, :include => [:superitems], :conditions => ["superitems.id is not null"])

the rails3 equivalent of this is

Item.includes([:superitem]).where("superitems.id is not null").all

This way you're pulling in the parent and testing if the id field on the superitem side of the join has an id. if it doesn't, it's because there's no superitem there (or, technically, it could be there but have no id. But this would normally never happen).

Max Williams
  • 32,435
  • 31
  • 130
  • 197
  • It doesn't work. I think it's because there's no `superitem` table (item relates to itself as superitem). `ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: superitems.id` – fetsh Jun 03 '11 at 13:02
  • Well. I think there are actually three questions in my post. 1) Is it possible to make chainable class methods. 2) How to pull only instances with parents using sql query. And 3) how to do it in case of self relation. You definitely answered the second one. So thank you =) – fetsh Jun 03 '11 at 13:20
  • ah yes, forgot that it was self referential, sorry. I think :dependent => :nullify is the way to go anyway :) – Max Williams Jun 06 '11 at 11:18
0

The following will get all the items with a parent, I'm not sure what you mean when you say "Although that item has superitem_id, superitem can be already destroyed"

items = Item.where("superitem_id IS NOT NULL")
Ant
  • 3,877
  • 1
  • 15
  • 14
  • I mean, that when I'm destroying superitem. Item still has superitem_id. @item.superitem_id # outputs 31 Item.find(31).destroy @item.superitem_id # still outputs 31 It means, that there is no superitem, so your `Item.where("superitem_id IS NOT NULL")` doesn't get all the items with a parent, but just all the items with a superitem_id – fetsh Jun 03 '11 at 11:22
  • @ilzoff Before deleting an item why not loop through all of its children and remove the superitem_id? that way you don't have any redundant data and you can keep things clean and simple? – Ant Jun 03 '11 at 11:26
  • Yeah. I definitely will do this. Especially now, because I've found `:dependent => :nullify` =) – fetsh Jun 05 '11 at 11:25