Rails - PG::DatatypeMismatch: ERROR - ruby-on-rails

So it's a very beginner question, I am getting the following error. I know that I need to convert it over to string type. I have tried many kinds of conversion method, but yet to work. Hard coding this field as something like '2018-01-01' works. The param content that I am getting back is correct. It is the type that is creating this error. I even try to convert it over to a date type and tested the type and returned true, but still fail to succeed. Let me know if this makes sense.
PG::DatatypeMismatch: ERROR: column "order_date" is of type date but expression is of type integer
My expresson
created_at = (params[:created_at])
Have tried
created_at = (params[:created_at]).to_date
created_at = (params[:created_at]).to_s
I am trying to get the params[:created_at] from the payload and use the following code to insert into psql. So I think I should go with the create action. Please let me know what I am doing wrong.
def create
created_at = (params[:created_at])
puts created_at
sql = "insert into api.salesorder(site, order_date,sale_type,sales_rep,terms,customer_number) values('WHS',#{created_at},'CUST','HOUSE','PRE','123456')"
results = ActiveRecord::Base.connection.execute(sql)
end

If you still want to use direct SQL (which I advise against in Rails, since Rails does the SQL for you), you need to change it to wrap your date in single quotes.
sql = "insert into api.salesorder(site, order_date, sale_type,sales_rep, terms, customer_number) values('WHS','#{created_at}','CUST','HOUSE','PRE','123456')"
A better solution would be to leverage Rails to do the SQL work for you. Something like this:
# in app/models/salesorder.rb
class Salesorder < ActiveRecord::Base
end
# in app/controller/salesorder_controller.rb
class SalesorderController < ApplicationController
def create
#salesorder = Salesorder.new
#salesorder.order_date = params[:created_at]
#salesorder.site = 'WHS'
#salesorder.sale_type = 'CUST'
#salesorder.sales_rep = 'HOUSE'
#salesorder.terms = 'PRE'
#salesorder.customer_number = '123456'
if #salesorder.save
redirect_to #salesorder, notice: 'Salesorder was successfully created.'
else
render :new
end
end
end

You could do something like created_at = Date.parse(params[:created_at]), but i don't think it's nescessary. Some more information might be helpful. Is your params an integer type?

Related

Array in Ruby on rails, undefined method `minimum'

#click = Missing.select{|d| d.click < 10000}
find = #click.minimum(:id)
#data = Missing.where(id: find)
dataSize = #data.size
#renderData = #data[dataSize - 1]
render :json => #renderData
Errors:
undefined method `minimum' for #<Array:0x00000004acd678>
Error code : find = #click.minimum(:id)
I have no idea why it is wrong.
render :json = #click is working.
find = Missing.minimum(:id) is working.
#click = Missing.select{|d| d.click < 10000} is returning an array of active record instances. This is why minimum can not be called against an array.
If you want the first record in the #click set then use #click.first.id. If you want the smallest (min) id then use #click.map(&:id).min. Looking at your code i'm not sure what you are trying to achieve?
On a side note, i'd avoid calling Missing.select{|d| d.click < 10000} because that is doing a full table scan on your Missing model's table. As Ruby Racer says, better to query with active record.
#click is an array, not an association.
Why don't you use activerecord query to get your find?
find = Missing.where('click < 10000').minimum(:id)

dynamic query for different values rails 4 activerecord

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.

Using a Variable Name as Attribute in Rails

I want to DRY up my Rails code by making a common method that will be reused. In order to do so, I have to make some field/attributes and the class name that is used in the code variables, so it can work for the three models (and their fields) with the same code. I tried to learn from this question and this one, but I haven't been able to get it to work.
In my model, I have this:
def self.update_percentages
update_percentages_2(User, "rank", "top_percent")
end
def self.update_percentages_2(klass, rank_field, percent_field)
rank_class = (klass.name).constantize
total_ranks = rank_class.maximum(rank_field)
top_5 = (total_ranks * 0.05).ceil
rank_class.find_each do |f|
if f.send("#{rank_field}") <= top_5
f.send("#{percent_field}", 5)
f.save
end
end
end
With this code, I get ArgumentError: wrong number of arguments (1 for 0). When I start commenting lines out to narrow down the problem, it seems that the f.send("#{percent_field}", 5) causes the error.
And if I add:
percent_field = (percent_field).constantize
I get: Name Error: wrong constant name top_percent.
Can someone please help me determine what I'm doing wrong?
If you want to assign to an attribute, you need the method name with the equal sign:
f.send("#{percent_field}=", 5)
Also, this:
rank_class = (klass.name).constantize
is equivalent to this:
rank_class = klass
I would rewrite your method to update all qualifying records in on transaction.
def self.update_percentages_2(klass, rank_field, percent_field)
top_5 = ( klass.maximum(rank_field) * 0.05).ceil
klass.where("#{rank_field} <= ?", top_5).update_all(percent_field => 5)
end
BTW
Here is an answer to your original question.

Converting Rails model to SQL insert Query?

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.

Rails - Conditional Query, with ActiveRecord?

Given a query like:
current_user.conversations.where("params[:projectid] = ?", projectid).limit(10).find(:all)
params[:projectid] is being sent from jQuery ajax. Sometimes that is an integer and the above works fine. But if the use selects "All Projects, that's a value of '' which rails turns into 0. which yields an invalid query
How with rails do you say search params[:projectid] = ? if defined?
Thanks
I think you may have mistyped the query a bit. "params[:projectid] = ?" shouldn't be a valid query condition under any circumstances.
In any case, you could do some sort of conditional statement:
if params[:project_id].blank?
#conversations = current_user.conversations.limit(10)
else
#conversations = current_user.conversations.where("project_id = ?", params[:project_id]).limit(10)
end
Although, I'd probably prefer something like this:
#conversations = current_user.conversations.limit(10)
#converstaions.where("project_id = ?", params[:project_id]) unless params[:project_id].blank?
Sidenotes:
You don't have to use .find(:all). Rails will automatically execute the query when the resultset is required (such as when you do #conversations.each).
Wherever possible, try to adhere to Rails' snakecasing naming scheme (eg. project_id as opposed to projectid). You'll save yourself and collaborators a lot of headaches in the long run.
Thanks but if the where query has lets say 3 params, project_id, project_status, ... for example, then the unless idea won't work. I'm shocked that Rails doesn't have a better way to handle conditional query params
EDIT: If you have multiple params that could be a part of the query, consider the fact that where takes a hash as its argument. With that, you can easily build a parameter hash dynamically, and pass it to where. Something like this, maybe:
conditions = [:project_id, :project_status, :something_else].inject({}) do |hsh, field|
hsh[field] = params[field] unless params[field].blank?
hsh
end
#conversations = current_user.conversations.where(conditions).limit(10)
In the above case, you'd loop over all fields in the array, and add each one of them to the resulting hash unless it's blank. Then, you pass the hash to the where function, and everything's fine and dandy.
I didn't understand why you put:
where("params[:projectid] = ?", projectid)
if you receive params[:project] from the ajax request, the query string shouldn't be:
where("projectid = ?", params[:projectid])
intead?
And if you are receiving an empty string ('') as the parameter you can always test for:
unless params[:projectid].blank?
I don't think i undestood your question, but i hope this helps.

Resources