NoMethodError When Using Database Column Aliases in Rails - ruby-on-rails

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?

Related

Rails - PG::DatatypeMismatch: ERROR

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?

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)

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.

:Condition statement and Ruby Rails

I'm trying to get these statements to work:
#all_ratings = ["G","PG","PG-13","R"]
#valid_ratings = params["ratings"]
#movies = Movie.find(:all , :conditions => {#valid_ratings[:rating.upcase] => "1"} )
but I am getting the error:
undefined method `to_sym' for nil:NilClass
when I should be getting a match.
An example input is:
"ratings"=>{"PG-13"=>"1"}
Where am I going wrong?
More info:
The table has three fields, the title, release date, and rating, and is very simple. The options for rating are stated above in #all_ratings.
Rails 3.x:
#all_ratings = ["G","PG","PG-13","R"]
#valid_ratings = params["ratings"]
# Is #valid_ratings the same as you example, "ratings"=>{"PG-13"=>"1"}?
# It would be easiest to pass a subset of #all_ratings such that the params
# get converted to something like this: "ratings"=>["G", "PG"]
Movie.where(:rating => valid_ratings).all
# SQL: SELECT * FROM movies WHERE rating IN ('G','PG')
I am not sure what you are trying to with :rating.upcase. Is there a variable named rating? :rating is a symbol. upcase is not a method on Symbol.
It tells you that you #valid_ratings in nil
You probably trying doing this?
#valid_ratings = Rating.find(params["ratings"])

how to write a ActiveRecord query in the controller using a db column

I am doing the following:
#group_coach = GroupCoach.find("groups_count < '9'" )
I have a groups_count column in my db that is being updated by a counter_cache => true method in the Group model.
I know this isn't right. Because the error it spits out: Couldn't find GroupCoach with ID=groups_count < '9'
I have reviewed the Rails Guides:
client = Client.find(10)
Client.where("orders_count = '2'")
The second option does run in the localhost but isn't actually returning a GroupCoach... It just returns groupcoach...
What is the proper syntax for this?
Have you tried:
#group_coach = GroupCoach.where("groups_count < 9")
To clarify: #group_coach will be a collection of records. So you can't call #group_coach.name because all that will do is give you "GroupCoach" (the name of the class). Instead, you would need to iterate over the items:
#group_coach.each do |group_coach|
puts group_coach.name
end

Resources