Given these models class Category < ActiveRecord has_many :category_assignments has_many :posts, :through => :category_assignments end class CategoryAssignment < ActiveRecord::Base belongs_to :post belongs_to :category # Has boolean column ''featured'' end class Post < ActiveRecord::Base has_many :category_assignments has_many :categories, :through => :category_assignments end I want to add a named_scope to Post that will return all featured posts. But the semantics of this named_scope vary depending upon whether I am calling the named scope on Post or on category.posts: Post.featured is simple enough. The definition named_scope :featured, :joins => :category_assignments, :conditions => {:category_assignments.featured => true } will return all posts for which any associated category_assignment record has featured = true. But when I take a category instance and call category.posts.featured I want to get all of the posts which are featured *in that category*, which requires restricting by category_id. To do this properly the named_scope code needs to be able to determine whether it''s being called in a context in which category_assignments has already been joined (and/or whether a category_id is specified in the conditions). I have not figured out how to do this. If I can detect whether or not the named_scope is being called within the context of an association then I can do something like the following, which changes named_scope semantics based on whether or not a category _argument_ is explicitly provided: named_scope :featured, lambda { |*args| if (category = args.first) { :joins => :category_assignments, :conditions => {:category_id => category.id, :featured => true} } else { :joins => :category_assignments, :conditions => {:featured => true} } end } Is there some way to inspect a joins or conditions hash to determine association state from within a named_scope? And if so, is it reliable regardless of the order of named_scope stacking (i.e. will it work for either category.posts.other.named.scopes.featured or category.posts.featured.other.named.scopes)? Thanks, Sven --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Frederick Cheung
2008-Nov-24 17:49 UTC
Re: How to make named_scope aware of association context?
On Nov 24, 5:43 pm, Sven <Sven....-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> Given these models > > class Category < ActiveRecord > has_many :category_assignments > has_many :posts, :through => :category_assignments > end > > class CategoryAssignment < ActiveRecord::Base > belongs_to :post > belongs_to :category > # Has boolean column ''featured'' > end > > class Post < ActiveRecord::Base > has_many :category_assignments > has_many :categories, :through => :category_assignments > end > > I want to add a named_scope to Post that will return all featured > posts. But the semantics of this named_scope vary depending upon > whether I am calling the named scope on Post or on category.posts: > Post.featured > is simple enough. The definition > named_scope :featured, > :joins => :category_assignments, > :conditions => {:category_assignments.featured => true } > will return all posts for which any associated category_assignment > record has featured = true. > > But when I take a category instance and call category.posts.featured I > want to get all of the posts which are featured *in that category*, > which requires restricting by category_id. To do this properly theIt should do that by itself. You shouldn''t need to do anything. Have you tried it? Fred --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
On Mon, Nov 24, 2008 at 12:49 PM, Frederick Cheung < frederick.cheung-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> It should do that by itself. You shouldn''t need to do anything. Have > you tried it? > > Fred >Yes, with code like this: named_scope :featured, :joins => :category_assignments, :conditions => [''category_assignments.featured = ?'', true ] When I call Post.featured it works fine. But when I call, category.posts.featured I get ActiveRecord::StatementInvalid: SQLite3::SQLException: ambiguous column name: category_assignments.post_id because Rails generates this query: SELECT "posts".* FROM "posts" INNER JOIN "category_assignments" ON category_assignments.post_id post.id INNER JOIN category_assignments ON posts.id category_assignments.post_id WHERE (("category_assignments".category_id = 1)) AND ((category_assignments.featured = ''t'')) As you can see, category_assignments is being joined twice. The named_scope needs to omit the category_assignments join when it gets called on a Category instance. I''m running this on Rails 2.1.1, by the way. -Sven PS: there was an error in my initial post. I specified :conditions => {:category_assignments.featured => true } where I ought to have written :conditions => [''category_assignments.featured = ?'', true] --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Frederick Cheung
2008-Nov-24 18:25 UTC
Re: How to make named_scope aware of association context?
On 24 Nov 2008, at 18:07, Sven Aas wrote:> On Mon, Nov 24, 2008 at 12:49 PM, Frederick Cheung <frederick.cheung-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org > > wrote: > It should do that by itself. You shouldn''t need to do anything. Have > you tried it? > > Fred > > Yes, with code like this: > > named_scope :featured, :joins => :category_assignments, > :conditions => > [''category_assignments.featured = ?'', true ] > > When I call > > Post.featured > > it works fine. But when I call, category.posts.featured I get > > ActiveRecord::StatementInvalid: SQLite3::SQLException: ambiguous > column name: category_assignments.post_id > > because Rails generates this query: > > SELECT "posts".* FROM "posts" > INNER JOIN "category_assignments" ON category_assignments.post_id > = post.id > INNER JOIN category_assignments ON posts.id = > category_assignments.post_id > WHERE (("category_assignments".category_id = 1)) > AND ((category_assignments.featured = ''t'')) > > As you can see, category_assignments is being joined twice. The > named_scope needs to omit the category_assignments join when it gets > called on a Category instance. >Oops. I skimmed over the definition of your scope and didn''t notice that there was a join. I''m going out on a limb here but from inside a procedural named scope then if there is a @proxy_owner instance variable or if it responds to proxy_owner/proxy_target then it;s an association. You could also check the current scope and see what joins are in there. Fred> I''m running this on Rails 2.1.1, by the way. > > -Sven > > PS: there was an error in my initial post. I specified > :conditions => {:category_assignments.featured => true } > where I ought to have written > :conditions => [''category_assignments.featured = ?'', true] > > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
That does seem promising, but I haven''t gotten it to work yet. Both proxy_owner and proxy_target remain undefined (on self) and @proxy_owner remains null regardless of which way I invoke the named_scope lambda. You also suggested checking the current scope for joins but that''s precisely the thing I don''t know how to do, which prompted my initial post. I''m still working on it, though. -Sven --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---