Introducing QuerySet!

2 views
Skip to first unread message

Flip Sasser

unread,
Apr 27, 2008, 7:51:55 PM4/27/08
to DeRailed - Denver Rails UG
Hi guys,

My name's Flip; I've posted a couple of times. I've spent the day
writing unit tests for my Django QuerySet clone for ActiveRecord. I've
since posted it to a few places and thought you'd be interested in
checking it out!

RubyForge project page: http://rubyforge.org/projects/queryset/
Github project page: http://github.com/flipsta/queryset/tree/master

I'll keep it short: it allows you to do lazy filter chaining (meaning
expand the complexity of a query without touching the database before
necessary) across multiple ActiveRecord::Base models. Check it:

BlogPost.filter(:user__first_name__contains => "Flip")

It may or may not be useful to anyone, at any time, but check it out
if you think it might!

Flip

PS. Thanks to Tom Anderson for giving me the courage to show off some
of my (kludgy) code

Tom Anderson

unread,
Apr 30, 2008, 12:44:11 AM4/30/08
to dera...@googlegroups.com
Hi Flip,

I'm not clear what the value proposition is for this... what does it give me that I can't already get from rails?   I am not familiar with the Django QuerySet but it sounds like it might be pretty similar to the "with_scope" method.   So a clear example of what this does or what problem it solves up front would be good.

In your example, how is it different than this?

   BlogPost.with_scope(:find => {:conditions => ["first_name like '%Flip%']})

~Tom

Flip Sasser

unread,
Apr 30, 2008, 11:45:04 AM4/30/08
to dera...@googlegroups.com
Hi Tom,

My apologies for the confusion. I'm not great at communicating these concepts, or writing documentation.

It's very different from with_scope. There are three benefits to using QuerySet:

1. It allows you to search across model-relationships without writing any SQL or managing the :include parameter.
2. It allows you to build on existing queries, making them as complex or simple as you need.
3. It never touches the database until explicitly asked to through find or implicitly through each, count, size, or empty?

Here's a better example. Let's say I have the following models:

class Account < ActiveRecord::Base
has_many :users
end

class User < ActiveRecord::Base
belongs_to :account
has_many :blog_posts
end

class BlogPost < ActiveRecord::Base
belongs_to :user
end

We want to offer the end-user a form with which to search for blog posts. They want to be able to search on the following criteria:
  • Blog posts whose title contains a string
  • Blog posts whose user_id is included via an array of checkboxes
  • Blog posts whose user's account is currently either active or inactive

So let's say the user fills out the form and our controller gets this hash:

params[:filter] => {:title => "Introducing", :user_ids =>  ["1","4","18"], :account_active => "0"}

The controller might do something like this:

blog_posts = BlogPost.filter(:title__contains => params[:filter][:title], :user_id__in => params[:filter][:user_ids], :user__account__active => params[:filter][:account_active])

Which would build the query - fairly straightforward. But let's say we have lots of conditions we're filtering on, we could do this generically:

@blog_posts = BlogPost.filter
for key,value in params[:filter]
if key.include?("title")
for name in value.split(" ")
@blog_posts.filter("#{key}__contains" => name) unless name == ""
end
elsif value != ""
@blog_posts.filter("#{key}#{"__in" if value.is_a?(Array)}" => value)
end
end

Furthermore, the developer could theoretically name the form filter elements to indicate how we want to build the QuerySet:

<input type="text" name="filter[user__account__name__contains]" />

... which would search for all blog posts whose user's account name contains the string the user passed in.

Last, but not least, QuerySet uses ActiveRecord's find and count methods, so everything is getting cleaned-for-SQL at the end of the day. This also gives users access to a standard :conditions array. Using the @blog_posts example above, we could paginate all @blog_posts built using the filter like so (assuming we have will_paginate installed, but this could be done manually as well through QuerySet's find and count methods):

@blog_posts = BlogPost.paginate(:conditions => @blog_posts.conditions, :page => params[:page], :order => "blog_posts.created_at")

Does that help clarify what it does?

Reply all
Reply to author
Forward
0 new messages