0

If I have the following classes

class User
    has_many :documents
end

class Document
    belongs_to :user
end

I would like to be able to do the following

User.where("id > 200").documents

which should generate SQL like

select * from documents
join users on documents.user_id == users.id
where users.id > 200

but activerecord is not that clever. Is this an unreasonable thing to expect to be possible out of the box?

== A possible anti DRY solution ==

class User
    has_many :documents
    def self.documents
        Documents.joins(:users).merge(self.scoped)
    end
end

but this is not very DRY as it seems to replicate the relations I have already defined.

bradgonesurfing
  • 30,949
  • 17
  • 114
  • 217

3 Answers3

1

No you can't do that with ActiveRecord. Rule of thumb - the class you start calling methods on, is the one that the returning objects are. So doing User.whatever will always return User objects. This pretty much eliminates the possibility to do what you want.

If you want to get Document objects, you need to start querying for them on the Document class instead. You could always define you user specific scopes in the User model and reuse them in your Document model for the sake of DRY.

class User < ActiveRecord::Base
  scope :foo, where("users.id > 200")
end

class Document < ActiveRecord::Base  
  belongs_to :user

  scope :bar, joins(:user).merge(User.foo)  
end

This effectively lets you use the scope defined in User (and which is user specific) in your Document model. I would also claim in that User.where("id > 200").documents makes less sense than Document.where("users.id > 200") (join skipped intentionally).

So I personally think you are just trying to approach this issue from the wrong end :)

Tanel Suurhans
  • 1,632
  • 12
  • 11
  • I understand that by default I cannot do this. However the information to do this is there. The association logic is encoded in the has_many declaration and this lets you traverse from an instance of a single object to a collection of a different type of object. I would like to know if there is something fundamental that would prevent you traversing from a collection ( active relation ) of one type to a collection of another type via a has_many relation. – bradgonesurfing Jul 03 '12 at 07:38
  • Without checking the code, I would say that the "Rule of thumb - the class you start calling methods on, is the one that the returning objects are." is one of the fundamental issues. The code would require quite a bit of changing to make it smart enough to be able to parse the last relation of an AR::Relation and use that as the object type. Even then I would disagree with with, as it would hamper readability. User.foo clearly shows what you get in return. User.documents.with_foo.pages.filled.lines.bold_and_italic.letters.bar does not :) – Tanel Suurhans Jul 03 '12 at 07:44
0

Try this:

Document.joins(:user).where('user.id > ?', 200)
xdazz
  • 158,678
  • 38
  • 247
  • 274
0

For your example classes you can just query the user_id field in the documents table:

Document.where("user_id > 200")
Stefan
  • 109,145
  • 14
  • 143
  • 218