We migrated our database from sqlite3 to postgreSQL. Now, at the following line, we get an error:
def self.get_table_id(id)
sql = "SELECT id FROM configtables WHERE parent = 'check' AND parentid = " + id.to_s
results = connection.execute(sql)
return nil if results.empty? # here's where the error happens
return results[0][0]
end
I have less knowledge of Ruby and ActiveRecords and with postgreSQL as well. Is the value in results a postgres-object, or whats #<PG::Result:0x007fcc82900a78> and what was it with the sqlite3 database?
This function is one of a few with raw sql-strings.
empty? is not defined for PG::Result:
PG::Result.instance_methods.include?(:empty?)
#=> false
To use empty? you should convert the result to an instance of Array:
results.to_a.empty?
Seems like you should "fetch" actual results from PG::Result object.
One way of doing this could be calling to_a
Try the following:
sql = "SELECT id FROM configtables WHERE parent = 'check' AND parentid = " + id.to_s
results = ActiveRecord::Base.connection.execute(sql).to_a
...
Related
Here is the query I am trying in my controller
query = []
if id
query = "category_id: #{id}"
end
#posts = Post.where(query)
But throwing error as ERROR: syntax error at or near ":"
Why this is not working any other way to do it
if id
query << {sub_category_id: id}
end
if test
query << {test_id: test}
end
#posts = Post.where(query)
Is there any way of doing like this
Change query to a hash instead of string:
if id
query = { category_id: id }
end
#posts = Post.where(query)
The reason query = "category_id: #{id}" did not work is because the supplied string is literally used in the query generated by ActiveRecord, i.e. your select query will have category_id: 1 (assuming id is 1) in the where clause. And this is not a valid SQL syntax.
Please read on how you can use strings in conditions following this link. Thanks to #RustyToms for suggesting the link.
Update: ( Add extra conditions to the query hash )
if id
query[:sub_category_id] = id
end
if test
query[:test_id] = test
end
#posts = Post.where(query)
Another way to do this:
#posts = Post.scoped
#posts = #posts.where(category_id: id) if id
(in case you're playing codegolf)
Edit: (this is definitely a side note that isn't at all relevant)
Your original solution relies on one of my least favorite features of Ruby. Consider the following code:
if false
a = 4
end
puts a
I would expect the puts a to fail with a NameError (undefined local variable "a"), but no! The Ruby parser hits a = and then initalizes its value to nil. So, despite the fact that there is no way for the innards of that if statement to run, it still impacts the other code.
The query I'd like to run is:
SELECT zcta.geoid10, ST_AsGeoJSON(ST_simplify(zcta.geom,500)) FROM zcta WHERE zcta.geoid10 = '90210'
However in the Rails console when I enter this:
testquery = "SELECT zcta.geoid10, ST_AsGeoJSON(ST_simplify(zcta.geom,500)) FROM zcta WHERE zcta.geoid10 = '90210'"
Zcta.find_by_sql testquery
I get the following returned:
=> [#<Zcta >]
If I do a basic query asking for the result of any column I get the response I expect. This only happens with PostGIS functions. Any idea what to do?
Alias the calculated column and you will get a method added to the returned objects.
Zcta.
select("*, ST_AsGeoJSON(ST_simplify(geom,500)) as my_geo").
where(geoid10: '90210').each do |result|
puts result.my_geo
end
Is there a way to convert a Rails model into an insert query?
For instance, if I have a model like:
m = Model.new
m.url = "url"
m.header = "header"
How can I get the corresponding SQL query ActiveRecord would generate if I did m.save?
I want to get: "INSERT INTO models(url, header) VALUES('url', 'header')" if possible.
Note: I don't want to actually save the model and get the query back (from log file, etc). I want to get the query IF I chose to save it.
On Rails 4.1, I found the below code snippet working:
record = Post.new(:title => 'Yay', :body => 'This is some insert SQL')
record.class.arel_table.create_insert
.tap { |im| im.insert(record.send(
:arel_attributes_with_values_for_create,
record.attribute_names)) }
.to_sql
Thanks to https://coderwall.com/p/obrxhq/how-to-generate-activerecord-insert-sql
Tested in Rails 3.2.13: I think I got it right this time, it definitely does not persist to the db this time. It also won't fire validations or callbacks so anything they change won't be in the results unless you've called them some other way.
Save this in lib as insert_sqlable.rb and you can then
#in your models or you can send it to ActiveRecord::Base
include InsertSqlable
Then it is model.insert_sql to see it.
#lib/insert_sqlable
module InsertSqlable
def insert_sql
values = arel_attributes_values
primary_key_value = nil
if self.class.primary_key && Hash === values
primary_key_value = values[values.keys.find { |k|
k.name == self.class.primary_key
}]
if !primary_key_value && connection.prefetch_primary_key?(self.class.table_name)
primary_key_value = connection.next_sequence_value(self.class.sequence_name)
values[self.class.arel_table[self.class.primary_key]] = primary_key_value
end
end
im = self.class.arel_table.create_insert
im.into self.class.arel_table
conn = self.class.connection
substitutes = values.sort_by { |arel_attr,_| arel_attr.name }
binds = substitutes.map do |arel_attr, value|
[self.class.columns_hash[arel_attr.name], value]
end
substitutes.each_with_index do |tuple, i|
tuple[1] = conn.substitute_at(binds[i][0], i)
end
if values.empty? # empty insert
im.values = Arel.sql(self.class.connectionconnection.empty_insert_statement_value)
else
im.insert substitutes
end
conn.to_sql(im,binds)
end
end
It turns out the code is in ActiveRecord::Relation and not ActiveRecord::Persistence. The only significant change is the last line which generates the sql instead of performing it.
If you dont want to save the model you call m.destroy when you are done with the object.
You can log the sql query by debugging it like this
Rails.logger.debug "INSERT INTO models(url, header) VALUES(#{m.url}, #{m.header}).inspect
After search a lot over the Internet and forums, I think I found a better solution for your problem: just requires two line of code.
I found a good gem that do exactly what you want, but this gem only works for Rails 3.2 and older. I talked with author and he doesn't want support this gem anymore. So I discovered by myself how to support Rails 4.0 and now I'm maintaining this gem.
Download the "models-to-sql-rails" gem here, supporting Rails 4.0 and older.
With this gem, you can easily do the following. (the examples inside values are just a joke, you will get the correct values when using it in your object).
For objects:
object.to_sql_insert
# INSERT INTO modelName (field1, field2) VALUES ('Wow, amaze gem', 'much doge')
For array of objets:
array_of_objects.to_sql_insert
# INSERT INTO modelName (field1, field2) VALUES ('Awesome doge', "im fucking cop")
# INSERT INTO modelName (field1, field2) VALUES ('much profit', 'much doge')
# (...)
Just see the Github of this project and you'll find how to install and use this wonderful gem.
Undefined method errors when I try to use a column alias for an aggregate (PostgreSQL)
Inside my model:
class B2bLoginAttempt < ActiveRecord::Base
set_table_name "b2b_logins"
end
Inside my controller:
#client_ip = request.env['REMOTE_ADDR']
#sql = "select count(id) as failed_logins FROM b2b_logins WHERE ip_address = '"+#client_ip+"'"
f = B2bLoginAttempt.find_by_sql(#sql)
failed_attempts = f.failed_logins.to_s
f.destroy
Then I see: undefined method `failed_logins' for #<Array:0x104d08478>
The error occurs because find_by_sql returns an array, so you need to write f.first.failed_logins.to_s instead of f.failed_logins.to_s.
Check find_by_sql doc here.
Not sure I am completely following your logic correctly, but it seems like you may be better off with this:
f = B2bLoginAttempt.where("ip_address = ?", #client_ip)
f.map(&:destroy)
You can get the actual count with f.count
Did I miss something?
I have a Category table and, based on knowing the category name (it is unique), I want to get able to get the ID.
In rails console I tried
cat = Category.where("name = ?", "Food")
which successfully found the object, but typing cat[:id] gives me "TypeError: can't convert Symbol into Integer". What am I doing wrong?
Try using find_by_name instead.
cat = Category.find_by_name('Food')
puts cat.id
where returns an array (well, array-like) object. So, you could instead use cat = Category.where('name = ?', 'Food').first. The find methods will do the same thing, but look cleaner too.
cat = Category.where("name = ?", "Food").first
id = cat.id