Access a query in Ruby on Rails - sql

I have in my controller this:
#itemsok = Search.where("first_item_id = ?", params["3"])
This is sopposed to be a query in the search table of the database asking for all the searches that have a first_item_id = 3 ...
Question 1 .- The syntax is I found it in http://guides.rubyonrails.org/active_record_querying.html but im not sure if im using it right?
Ok the question 2 is, I have this on the controller, is it ok to have querys in the controller?
In the view im printing the variable <%= #itemsok %> and all I get is a
ActiveRecord::Relation:0x007fd3d3e894d8
Any suggestions?
Thanks in advance.

ActiveRecord 3 lets you chain relations together so you can do something like this:
#itemsok = Search.where("first_item_id = ?", params["3"]).where("foo = ?", "bar")
The where() function returns an ActiveRecord::Relation. Generally this isn't a problem, since if you use the object it'll automatically run the query and return the results on the object so you'll get the database objects. AR doesn't run the query until it's actually needed.
Where will return a list of items (Array), so if you're just debugging, change your view to this:
<%= debug #itemsok.to_a %>

You seem to be constructing the query wrong way.
If you want to search for records with first_item_id = 3, you should do:
Search.where("first_item_id = ?", 3)
This will return an array of matching records, something you can't easily print with <%= #itemsok %>. You should iterate over the elements and print each one:
<% #itemsok.each do |item| %>
<%= item.name %>
<% end %>
I'd also suggest defining to_s method for the objects you want to print.
class Search
def to_s
name
end
end
Then you can simply print the object and to_s method will be automatically called for you:
<% #itemsok.each do |item| %>
<%= item %>
<% end %>

The right way to do is to define a namedscope in the model and then use it in the controller.
Something similar to this :
class Search < ActiveRecord::Base
named_scope:item_ok,lambda {|*args|{:conditions=>["item_id >= ?", args.first]}}
end
and then call the namedscope from the controller like this :
#itemsok = Search.item_ok(params[:value])

Related

Counting associations where child model's attribute = x

Trying to get a count of Driver's cars when manual transmission = true. (Driver has_many cars; Car belongs_to driver, etc.)
My current code:
<% #driver = Driver.find(1) %>
<% driver.cars.where("Car.manual = true").count %>
Returns this error:
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "car"
LINE 1: ...ars" WHERE "cars"."driver_id" = $1 AND (Car.m...
^
: SELECT COUNT(*) FROM "cars" WHERE "cars"."driver_id" = $1 AND (Car.manual= true)
When I remove the ".count", it does seem to find a relationship, because it prints:
#<Car::ActiveRecord_AssociationRelation:0x007fea6ddf4c88>
I also tried
<%= #driver.cars.where(manual = true).count %>
But that returns a count of all the driver's cars.
I suspect the problem is with my "manual = true" syntax, but I'm fairly new to writing queries so I'm probably missing something stunningly obvious. If anyone can help me figure out where I'm going wrong, I'd appreciate it. (Or, of course, if there's a better way to do this.)
The Driver class should declare the relationship:
class Driver
has_many :cars
# other things
end
And then, you have to pass a hash to the where conditions:
<%= #driver.cars.where(manual => true).count %>
or using the new ruby hash syntax:
<%= #driver.cars.where(manual: true).count %>
You can always debug the query being sent to the database with
<%= #driver.cars.where(manual: true).to_sql %>

rails if result in scope

I have a scope on my user model
I want to use this scope within a block on a view to display an option
my scope looks like this
scope :can_own_project, where('superuser = ? OR projectadmin = ?', true, true)
in my view I can achieve what I am looking to do by:
#stdprojectusers.each do |projectuser| %>
<% if (projectuser.superuser == true) || (projectuser.projectadmin == true) %>OPTION<%end%>`
what I would like to do is something like
<% if projectuser.can_own_project %> OPTION <% end %>
or
<% if projectuser == User.can_own_project %> OPTION <% end %>
any advise?
thanks
I don't think you want a scope. Scope's are applied to classes. If I'm reading you right, you are working with an instance. Is there a reason you can't simply define a method on that class?
class ProjectUser << ActiveRecord::Base
def can_own_project?
superuser == true || projectadmin == true
end
end
Note, I changed your method and appended a '?'. It's a habit of mine and isn't necessary, but I like the question form myself.
Your scope should work, but performance will (over time) take a major hit. What you'd want to do is:
<% if User.can_own_project.include?(projectuser) %> OPTION <% end %>
What I think you're looking for is a helper method...
module UserHelper
def does_user_own_project?(user)
user.superuser || user.projectadmin
end
end
Your view could then look like:
#stdprojectusers.each do |projectuser| %>
<% if does_user_own_project?(projectuser) %>OPTION<%end%>
If you'll want to use this outside the scope of this view, you could also make it an instance method on User:
class User < ActiveRecord::Base
def does_user_own_project?
self.superuser || self.projectadmin
end
end

Learning to Search in Rails

I'm trying to create a search form in my rails application. I've looked up various solutions but they make little sense to me.
I'm getting the following error when I run a search through a form in my rails app. Right now my concern (other than the error) is my instance variable #computers in my index action. I'm pretty sure it's not 'the rails way' to get a search done properly and would love some advice.
Error
undefined method `%' for #<Array:0x5780460>
Parameters after Search
http://localhost:3000/computers?utf8=%E2%9C%93&direction=&sort=&search=bob
Search Form
<%= form_tag computers_path, method: "get" do %>
<%= hidden_field_tag :direction, params[:direction] %>
<%= hidden_field_tag :sort, params[:sort] %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Go", name: nil, class: "btn btn-primary" %>
<% end %>
Call to Method
def index
#computers = Computer.where(school_id: current_user.school_id).search(params[:search]).category(params[:category]).order(sort_column + " " + sort_direction)
end
Method
def Computer.search(search)
if search
search = search.downcase
params = []
values = {}
column_names.each do |c|
params << "#{c} LIKE #{c.to_sym}"
values[c.to_sym] = search
end
params.join (' OR ')
where(params,values)
else
all
end
end
You've got the right idea, but invoking the .join method does not change the object on which it is called, it merely returns a string representation. You need to store the return in a variable, something like this: paramsStr = params.join(' OR '). Then simply pass paramsStr to the where clause.
Ultimately, that is what is causing your unidentified method % for Array .... error; this version of the where method is expecting the first parameter to be a string. Check out this documentation, the part about placeholder conditions.
Hope that helps.

rails OR query based on multiple checkbox selections

This seems like it should be a common problem but I'm having trouble finding an answer. Basically I want to have a form with 10 or so checkboxes which I'm creating with check_box_tag. When the form is submitted I want to generate a query that return all records that match ANY of the checked selections. So, the number of checked selections will vary.
So, for example, if I have
class Book < ActiveRecord::Base
belongs_to :author
end
I want to generate something like
Book.where("author_id = ? or author_id = ?", params[authors[0]], params[authors[1]]) if there are two boxes checked, etc.
Thanks for any insight.
Will this work for you?
Book.where(author_id: [array_of_author_ids])
You need to collect author_ids from params first
I recently had to do something similar, this is how I achieved this. It's pretty clever (at least I think so. :))
I created a query model that serializes the query column (text field) in JSON. I use a form to get the query data from the user with selection fields.
class BookQuery < ActiveRecord::Base
has_many :books
# loop through each foreign key of the Book table and create a hash with empty selection
def self.empty_query
q = {}
Book.column_names.each do |column_name|
next unless column_name.ends_with?("_id")
q.merge column_name => []
end
end
end
I'm using Author as an example below:
<%= form_for #book_query do |f| %>
<% for author in Author.all %>
<%= check_box_tag "book_query[query][author_ids][]", author.id, false%>
<%= author.name %>
<% end %>
<%= f.submit "Save Query" %>
<% end %>
When this form is submitted you ended up with parameters like this:
When the form is submitted it generates this parameter:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"XXXXXXXXXXX", "book_query"=>{"query"=>{"author_ids"=>["2", "3"]}}, "commit"=>"Save Query"}
Now in the BookQuery controller's create action you can just do what create function always does:
def create
#book_query = BookQuery.build(params[:book_query])
if #book_query.save
flash[:success] = "Book query successfully saved."
redirect_to ...
else
flash[:error] = "Failed to save book query."
render :new
end
end
But by default rails serializes the data in hash type:
1.9.3p194 :015 > pp BookQuery.find(9).query
BookQuery Load (0.7ms) SELECT "book_queries".* FROM "book_queries" WHERE "book_queries"."id" = $1 LIMIT 1 [["id", 9]]
"--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nauthor_ids:\n- '2'\n- '3'\n"
=> "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nauthor_ids:\n- '2'\n- '3'\n"
In BookQuery model, add the following:
serialize :query, JSON
But rail would change the IDs to string:
1.9.3p194 :018 > query = JSON.parse(BookQuery.find(10).query)
BookQuery Load (0.5ms) SELECT "book_queries".* FROM "book_queries" WHERE "book_queries"."id" = $1 LIMIT 1 [["id", 10]]
=> {"author_ids"=>["2", "3"]}
1.9.3p194 :019 > query["author_ids"]
=> ["2", "3"]
What I did then is override the attribute accessors in BookQuery model:
The below has to be done because the hash returns strings, not ids in integer.
def query=(query)
query.each_pair do |k, v|
if query[k].first.present?
query[k].map!(&:to_i)
else
query.except!(k)
end
end
write_attribute(:query, query)
end
# just want to avoid getting nil query's
def query
read_attribute(:query) || {}
end
To find book with this query, you can simply add this function to your Book model:
def self.find_by_book_query(book_query, options = {})
options[:conditions] = book_query.query
find(:all, options)
end
Now you get a customizable query string based on the model definition Book and everything works like the Rails way. :)

Rails filtering with acts_as_taggable gem

I am working the acts-as-taggable-on gem and have a question about how to filter down search results based on tags users select. Here's an abridged look at my controller:
class PhotosController < ApplicationController
def index
#photos = Photo.where(["created_at > ? AND is_approved = ?", 1.months.ago, true])
#tags = ["Animals", "Architecture", "Cars", "Flowers", "Food/Drink", "General", "Landscape", "Mountains", "Nature"]
end
def search_by_tag(tag)
#photos = Photo.where('tagged_with LIKE ?', tag)
end
end
Photos/index
<% #tags.each do |tag| %>
<%= link_to tag, {:search_by_tag => tag}, :class => "tag" %>
<% end %>
This lists out all of the tags from the hash #tags defined in index, but clicking them doesn't actually filter anything down. Here's a look at what clicking one of those links produces in the log:
Started GET "/photos?search_by_tag=Animals" for 127.0.0.1 at Sun Oct 09 17:11:09 -0400 2011
Processing by PhotosController#index as HTML
Parameters: {"search_by_tag"=>"Animals"}
SQL (0.5ms) SELECT name FROM sqlite_master WHERE type = 'table' AND NOT name = 'sqlite_sequence'
The result I want is for the page to display only Photos that are tagged_with whichever tag was clicked on. I can't quite figure out how to accomplish this.
(Side-question: I can't seem to find a way to list out all of the tags from the tags table that acts-as-taggable-on generated. Doing something like Photo.tagged_with or Photo.tags doesn't work. I am able to see the "tags" table the gem created, and the entries inside of it; I'm just not really clear how to handle that using this gem)
Any thoughts greatly appreciated.
UPDATE
I've updated my code and am a bit closer.
class PhotosController < ApplicationController
def search_by_tag
#photos = Photo.tagged_with(params[:tag_name])
end
photos/index
<% Photo.tag_counts_on(:tags).map(&:name).each do |tag| %>
<%= link_to tag, {:action => 'search_by_tag', :tag_name => tag}, :class => "tag" %>
<% end %>
I believe this is closer, but still working through this...
You have a number of errors in your code:
Your link_to call is actually calling the index action.
Your search_by_tag method is expecting an argument, where it should be using the params hash to access the parameters passed to it in the web request.
tagged_with is a class method added by acts_as_taggable_on, not a field in your table - therefore you can't use it in the where method like you have done.
Finally, to get all the tag names: Photo.tag_counts_on(:tags_or_whatever_you_tagged_on).map(&:name)
Take a look at the acts_as_taggable_on documentation and you'll see examples of how to use tag_counts_on and tagged_with.
As for the Rails things: http://guides.rubyonrails.org/ http://railsforzombies.org/ and/or http://railscasts.com/