I have a Model called Status, its handling a table with two columns Stat and Colour.
Since these columns are also Model methods I would expect the following to work without an error
#a = Status.where(:stat => "Operational")
#a.colour = "Green"
However when I call #a.colour I receive an error stating that the method 'colour=' does not exist.
I am calling #a.colour from within seeds. This is just a model, it does not have a controller with it.
What am i doing wrong?
--Edit--
Model
class Status < ActiveRecord::Base
end
schema
create_table "statuses", :force => true do |t|
t.string "stat"
t.string "colour"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
Is this what you requested? I did not fully understand the request,
Kind Regards
I suppose Status.where() returns more than one record. So you are trying to call the color= method on an array which obviously does not exist!
So you need to iterate trough all found records, using
Status.where(:stat => "Operational").each do |a|
a.colour = "Green"
end
For more information check the Rails ActiveRcord Query Interface guide, it tells you:
If you’d like to add conditions to your find, you could just specify them in there, just like Client.where("orders_count = '2'"). This will find all clients where the orders_count field’s value is 2.
Related
I am trying to query multiple Active Record-Models by passing query-params to a Controller. Within my tales_controller.rb I have the following index-method:
def index
#tales_count = Tale.all.count
if params[:search]
#tales = Tale.joins(:category)
.where('category.title ILIKE ?', "%#{params[:search]}%")
.where(
'title ILIKE :search OR
subtitle ILIKE :search OR
short_description ILIKE :search', search: "%#{params[:search]}%"
)
else
#tales = Tale.all
end
render template: 'tales/index'
end
Now, I can't seem to figure out the correct solution to this problem, as for the most part PG throws an error, saying: PG::AmbiguousColumn: ERROR: column reference "title" is ambiguous. I sense that this is due to the fact that I try to query the title-field on the Tale-, as well as on the Category-Model. However I am not able to fix this problem myself.
By providing the index-method with the right queries I expect to be able to query a couple fields on the Tale-Model (namely title, subtitle and short_description and potentially more), as well as the title-field on the Category-Model.
The Category-Model is referenced by the Tale-Model. This is what the schema.rb looks like:
create_table "tales", force: :cascade do |t|
t.string "public_id"
t.string "title"
t.string "subtitle"
t.string "player_count"
t.string "short_description"
t.datetime "published_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "category_id"
t.bigint "author_id"
t.index ["author_id"], name: "index_tales_on_author_id"
t.index ["category_id"], name: "index_tales_on_category_id"
end
EDIT: uhm, I just realized that by querying the way I currently do, I expect the category.title AND any of the other Tale-fields to carry the search-term. This is not want I intended, frankly.
Table name in Rails are by convention in the plural. So change this to read
.where('categories.title ILIKE ?', "%#{params[:search]}%")
and just for fun ILIKE in Postgres can be written as
.where('categories.title ~* ?', params[:search])
I'm working on a questionnaire for users to fill out. Users can answer each question many times. Responses have a question_key to pair them with their question. I want to get the full set of a user's current responses without retrieving every response created by a user.
The responses table looks like this:
create_table "responses", :force => true do |t|
t.string "question_key", :null => false
t.text "value", :null => false
t.integer "user_id", :null => false
t.datetime "created_at"
t.datetime "updated_at"
end
Here's the relevant part of the response class:
class Response < ActiveRecord::Base
def self.current
recent.uniq {|response| response.question_key}
end
scope :recent, order('created_at DESC')
end
user.responses.current gets the set of responses, but it seems really inefficient. It's getting all of the responses ever created by a user, and then throwing away everything but the most recent one for each unique value of question_key
I did figure out a SQL query that does what I want, ordering the rows before doing GROUP BY. Is there a reasonable way to use this in Response::current?
SELECT *
FROM (
SELECT *
FROM responses
WHERE user.id = 6
ORDER BY created_at DESC ) as user_responses
GROUP BY question_key
You can perform a group by in active record by using the group method.
Responses.where(user_id: '123').order('created_at DESC').group('question_key')
So your code would be something like:
class Response < ActiveRecord::Base
scope :recent, order('created_at DESC')
def self.current
recent.group('question_key')
end
end
This solution relies on the ids being sequential:
class Response < ActiveRecord::Base
def self.current
where(id: group(:question_key).maximum(:id).values)
end
end
It results in two queries when getting the current responses for a user (user.responses.current), but it avoids returning all of the user's responses.
SELECT MAX(`responses`.`id`) AS maximum_id, question_key AS question_key FROM `responses` WHERE `responses`.`user_id` = 6 GROUP BY question_key
SELECT `responses`.* FROM `responses` WHERE `responses`.`user_id` = 6 AND `responses`.`id` IN (452, 447, 9, 225, 190, 230, 240)
How would I go about doing this? Is it advisable?
I have a table called "Item" with columns "name" and "price".
Price changes all the time and I want to store all the changes so I can graph them.
I was thinking "price" should be an array? Is this correct?
Thanks
I would use a separate table to track the price history. Something simple like this:
create_table :item_prices do |t|
t.integer :item_id, :null => false
t.decimal :price, :null => false, :precision => 7, :scale => 2
t.timestamps
end
Note that the price is a decimal, not a float. Never ever use floating point for money.
Then in Item, something like this:
class Item
has_many :item_prices
before_save :update_price_history, :if => :price_changed?
private
def update_price_history
self.item_prices.create!(:price => self.price)
end
end
A nice advantage of this is that you track when the price changed as well as the price itself, that might make your graph look a bit more sensible.
I am trying to do a query for all cities (selecting only their name attribute) by their ID, and I want to be able to specify a range of ID's to select. My code is below:
def list_cities(start, stop)
cities = City.all(order: 'name ASC', id: start..stop, select: 'name')
cities.map { |city| "<li> #{city.name} </li>" }.join.html_safe
end
However, I get an error:
Unknown key: id
My implementation in my view is:
<%= list_cities(1,22) %>
This is a helper method to be put in all views, so I am not putting the logic in a particular controller.
My schema for this model is:
create_table "cities", :force => true do |t|
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "neighborhoods"
t.string "name"
t.integer "neighborhood_id"
end
When I ran the method in my console, I got:
City Load (0.9ms) SELECT name FROM "cities" WHERE ("cities"."id" BETWEEN 1 AND 3) ORDER BY name ASC
=> ""
I know it's not an issue of having an empty database since it worked with the following version of the method:
def list_cities(start, stop)
cities = City.all(order: 'name ASC', limit: stop - start, select: 'name')
cities.map { |city| "<li> #{city.name} </li>" }.join.html_safe
end
However, this method returns only the first 'n' records and not a range like I want.
When trying a simpler query in the console:
1.9.3p385 :009 > City.where(:id => 1..4)
City Load (0.9ms) SELECT "cities".* FROM "cities" WHERE ("cities"."id" BETWEEN 1 AND 4)
=> []
I figured out why it was happening...
I did City.all in my console and realized that my cities started with id "946" because I had seeded multiple times and the ID's were not what I thought they were! The solution offered was correct!
City.where(:id => start..stop).order('name ASC').select(:name)
You can turn your query to the following:
cities = City.all(order: 'name ASC', conditions: { id: start..stop }, select: 'name')
Speransky Danil's answer should work perfectly. you can try this too:
City.find((start..stop).to_a,:select=>:name,:order=>'name ASC')
In a Rails(3.2) app, I have a class method on a Model like this:
def import(level, max = 10)
db = ActiveRecord::Base.connection
result = db.execute("SELECT word FROM levels WHERE level == #{level} AND word NOT IN (SELECT entry FROM words) limit #{max};");
It just imports 10 new words(create 10 records) at a time that do not exist as Word record yet.
The schema looks something like this:
create_table "levels", :force => true do |t|
t.string "word"
t.integer "level"
end
create_table "words", :force => true do |t|
t.string "entry"
t.integer "level", :default => 0
t.text "definition"
t.string "thesaurus", :default => "none"
end
I'm an SQL noob. Messing with rails dbconsole(sqlite3, I'm using sqlite3 on a server as well), I somehow came up with the raw sql query above. I sort of know that I can do the same thing with Arel. How am I supposed to construct the query with ActiveRecord?
The following (untested) should work. It uses pluck in the subquery.
Level.where(:level => level).where("word NOT IN (?)", Word.pluck(:entry)).limit(max)
#Gazler's solution looks like it works, but I'll provide an alternative using MetaWhere syntax which is a bit more concise:
Level.where(:level => level, :word.not_in => Word.pluck(:entry)).limit(max)