Rails find method - select columns from ":include" table parameter - ruby-on-rails-3

The below find method returns all records but doesn't include venues table columns. Is there a way I can modify the below to include columns from venues? I tried :select => 'venues.id', and :select => 'venues.*', to no avail. Perhaps this is a limitation with the rails find method. Can the query be rewritten as perhaps "Item.where" to include all columns?
#vehicles = Vehicle.find(:all,
:select => '*',
:joins => [:vehicle_status, :category, :user],
:include => [:venue],
:conditions => ["vehicles.client_id = ? ", current_user.client_id]
)
I'm using rails 3.2.11.
Also, my when I use :select => 'vehicles.*, venues.*' and get the follow query on the console where it shows venues fields being selected, but they do not appear in the returned json:
SELECT "vehicles"."id" AS t0_r0, ... "vehicles"."created_at" AS t0_r33,
"vehicles"."updated_at" AS t0_r34, "venues"."id" AS t1_r0, "venues"."client_id" AS t1_r1,
"venues"."venue_name" AS t1_r2, "venues"."created_at" AS t1_r3, "venues"."updated_at" AS t1_r4
FROM "vehicles" INNER JOIN "vehicle_statuses" ON "vehicle_statuses"."vehicle_status_code" =
"vehicles"."status_code" INNER JOIN "categories" ON "categories"."id" =
"vehicles"."category_id" INNER JOIN "users" ON "users"."id" = "vehicles"."created_at_user_id"
LEFT OUTER JOIN "venues" ON "venues"."id" = "vehicles"."venue_id" WHERE (vehicles.client_id =
500 )
However, if I use :select => '*', the console shows two queries: one for the vehicles table, the other for venue: Venue Load (0.3ms) SELECT "venues".* FROM "venues" WHERE "venues"."id" IN (5000)

You should try
format.json { render json: #vehicles, :include => :venue }
You could also do a join
#vehicles = Vehicle.where(client_id: current_user.client_id).joins(:category, :vehicle_status).includes(:venue)
venue or venues depending on has_many or has_one. For further information lookup stuff here http://guides.rubyonrails.org/active_record_querying.html

With input from shishirmk, especially on the JSON response, here's the answer:
#where version
#vehicles = Vehicle.where("vehicles.client_id = ?", current_user.client_id )
.joins(:category, :vehicle_status, :user)
.includes(:venue)
.select("vehicles.id, vehicles.description, categories.category_description, venues.venue_name, users.email")
------
#find version
#vehicles = Vehicle.find(:all,
:select => 'vehicles.id, vehicles.description, categories.category_description, venues.venue_name, users.email',
:joins => [:vehicle_status, :category, :user],
:include => [:venue],
:conditions => ["vehicles.client_id = ?", current_user.client_id])
-----
format.json { render json: #vehicles, :include => [:venue, :category, :vehicle_status, :user ] }
Key lessons:
1) if you include specific columns in your select, child/parent tables won't be JSON root wrapped, and you can simply refer to fields as "venue_name" as opposed "venues.venues_name". So I STRONGLY recommend using SELECT and specifying columns when joining multiple tables with WHERE or FIND. You want to do this since multiple tables will likely have an "id" column.
2) you must be explicit with what your JSON returns (thanks #shishirmk). See my example for returning multiple tables/models.
3) Rails needs better documentation on these types of queries, especially since the framework frowns against using database views :-)

Related

Rails 3 ActiveRecord Query questions

I've implemented "following" function. Showing "people user A is following" was simple, but showing "people who are following user A" is giving me troubles.
I have follow_links, which have id, user_id, and follow_you_id column. When user A begins following user B, the columns will be like (user_id = A, follow_you_id = B).
To show users that A(#user) is following, I can simply do
#follow_yous = #user.follow_yous
But I'm not sure how to show users who are following A(#user)
To do this, I first found all the related links.
#follow_links = FollowLink.where(:follow_you_id => #user.id)
Now I thought I could just do #follow_mes = #follow_links.users, but it says user is an undefined method. So I guess I can either call user.follow_yous or follow_you.users.
My next approach was
#follow_links = FollowLink.where(:follow_you_id => #user.id)
#follow_mes = User.where(:id => #user.id, :include => #follow_links)
I intended to find all the User objects that had the provided #follow_links objects, but I think the syntax was wrong. I couldn't find a decent solution after a bit of research. I'd appreciate any help.
Update:
FollowLink model
belongs_to :user
belongs_to :follow_you, :class_name => "User"
You can use joins like this:
#users = User.joins(:follow_links).where(:follow_links => { :follow_you_id => #user.id })
you can use following:
#follow_links = FollowLink.where(:follow_you_id => #user.id)
#follow_links.collect(&:user) # :user should be the name of your relation to user in your followlink model
=> [User1, User2,...]

using :include && :conditions in a ruby on rails SQL request?

Hey I'm trying to figure something out..
I want to get all Carts that have a cart_stage.stage equal to '35' (this is a separate table and a cart has many stages)
the cart_stage table is a bit like
id ----- cart_id ----- stage
1 ------- 123 ---------- 20
2 ------- 123 ---------- 35
3 ------- 102 ---------- 35
I am trying this at the moment:
# Cart model
has_one :top_stage, :foreign_key => 'cart_id', :class_name => "CartStage", :order => 'stage'
# Cart controller
#carts = Cart.find :all, :order => 'created_at DESC', :include => :top_stage, :conditions => ["top_stage.stage = ?", 35]
This gives me :
SQLite3::SQLException: no such column: top_stage.stage: SELECT DISTINCT "carts".id FROM "carts" WHERE (top_stage.stage = 35) ORDER BY created_at DESC LIMIT 40 OFFSET 0
Hope it all makes sense and any help would be greatly appreciated,
Alex
That should probably be:
#carts = Cart.find(:all, :order => 'carts.created_at DESC', :include => :top_stage, :conditions => { "cart_stages.stage" => 35 })
Remember that you use the name of the table in the conditions, not the name of the association, however you do use the name of the association in the include option.
Whenever possible, you should probably use the hash method for expressing conditions to keep your declarations simple. It's when you need complicated OR or > type logic that the array-style proves necessary.
A better way of expressing it for Rails 3 is:
#carts = Cart.order('carts.created_at DESC').include(:top_stage).where('top_stages.stage' => 35)
It's not clear why your cart_stages table isn't being added with a JOIN.
Anywhere that you include literal SQL, you need to use the real table and column names, not the Rails association names. So you need to have: :conditions => ["cart_stage.stage = ?", 35] instead of top_stage
I managed to work it out my self actually.. What I wanted was -
#carts = Cart.find :all, :order => 'created_at DESC', :joins => :top_stage, :conditions => { :cart_stages => { :stage => 35 } }
Thanks for all the help guys! :)

Will_Paginate and order clause not working

I'm calling a pretty simple function, and can't seem to figure out whats going on. (I'm using rails 3.0.3 and the master branch of 'will_paginate' gem). I have the following code:
results = Article.search(params) # returns an array of articles
#search_results = results.paginate :page => params[:page], :per_page=>8, :order => order_clause
No matter what I make the order_clause (for example 'article_title desc' and 'article_title asc'), the results are always the same in the same order. So when I check using something like #search_results[0], the element is always the same. In my view, they are obviously always the same as well. Am I totally missing something?
I'm sure its something silly, but I've been banging my head against the wall all night. Any help would be much appreciated!
Edited to Add: The search clause does the following:
def self.search(params)
full_text_search(params[:query].to_s).
category_search(params[:article_category].blank? ? '' : params[:article_category][:name]).
payout_search(params[:payout_direction], params[:payout_value]).
length_search(params[:length_direction], params[:length_value]).
pending.
distinct.
all
end
where each of these guys is a searchlogic based function like this:
#scopes
scope :text_search, lambda {|query|
{
:joins => "INNER JOIN users ON users.id IN (articles.writer_id, articles.buyer_id)",
:conditions => ["(articles.article_title LIKE :query) OR
(articles.description LIKE :query) OR
(users.first_name LIKE :query) OR
(users.last_name LIKE :query)", { :query => "%#{query}%" }]
}
}
scope :distinct, :select => "distinct articles.*"
#methods
def self.payout_search(dir, val)
return no_op if val.blank?
send("payment_amount_#{dir.gsub(/\s+/,'').underscore}", val)
end
def self.length_search(dir, val)
return no_op if val.blank?
send("min_words_#{dir.gsub(/\s+/,'').underscore}", val)
end
Thanks.
If you look at the example from the will_paginate github page you can spot one important difference between their use of the :order clause and yours:
#posts = Post.paginate :page => params[:page], :order => 'created_at DESC'
This calls paginate on the Post object (with no objects being selected yet - no SQL has been executed before paginate comes along). This is different in your example: as you state in the first line of code "returns an array of articles". The simplest I can come up with showing the problem is
results = Model.limit(5).all
#results = results.paginate :order => :doesnt_matter_anymore
won't sort, but this will:
results = Model.limit(5)
#results = results.paginate :order => :matters
It should suffice to take the all out of the search method. It makes ActiveRecord actually perform the SQL query when calling this method. Will_paginate will do that for you when you call paginate (if you let it...). Check out the section on Lazy Loading in this post about Active Record Query Interface 3.0

How do I find records matching attribute with 1+ values in ActiveRecord/SQL?

How do I find records matching attribute with 1+ values in ActiveRecord/SQL? Examples would be something like:
Post.find_by_type("Post and/or ChildPost")
Post.find(:type => "Post and/or ChildPost")
How can I do this? The number of values will be no more than 10 I'd say.
Post.find :all, :conditions => ['type IN (?)', ['Post', 'ChildPost']]
Or:
values = ['Post', 'ChildPost']
Post.find :all, :conditions => ['type IN (?)', values]
That should produce the following SQL:
SELECT * FROM `posts` WHERE `type` IN ('Post', 'ChildPost');

Correct this Rails/ruby method for me, please?

I've a post model with act-as-taggable-on gem. Both tables have timestamps.
I started with
def tags
#posts = current_user.posts.find_tagged_with(params[:tag], :order => "#posts.tags.updated_at DESC"])
end
And when that didn't work, I tried changing things and ended up with this mess.
def tags
#posts = current_user.posts.find_tagged_with(params[:tag])
#tags = #posts.tags.all
#posts = #tags(params[:tag, :order => "#posts.tags.updated_at DESC"])
end
I basically want to sort by when the tags was last updated.
Bonus: Sort by tag.updated_at or post.updated_at, but in this particular app, I'll be updating tags the most, so just first one will be fine.
Any help is appreciated.
You have to join the tags table in your find statement:
def tags
#posts = Post.find_tagged_with(
params[:tag],
:conditions => {:user_id => current_user.id},
:joins => :tags,
:order => 'tags.updated_at DESC',
:group => 'posts.id'
)
end
Note: Find the right conditions to select only posts from the current user. This example could work, though.