Rails 5 scope issue - ruby-on-rails

In rails 5 with Postgresql, I'm writing a scope to select Status items by date. My model is:
class Status < ApplicationRecord
scope :in_month, ->(date){where("status_date = ?", date) if date.present?}
end
However, in rails console, I'm getting the following error when trying to use the scope.
```
2.4.0 :010 > Status.in_month("2018-06-01")
Status Load (11.7ms) SELECT "statuses".* FROM "statuses" WHERE (status.status_date = '2018-06-01') LIMIT $1 [["LIMIT", 11]]
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing FROM-clause entry for table "status"
LINE 1: SELECT "statuses".* FROM "statuses" WHERE (status.status_da...
^
: SELECT "statuses".* FROM "statuses" WHERE (status.status_date = '2018-06-01') LIMIT $1
Note that for some reason, it's trying to access table "status", not "statuses".
What the heck am I doing wrong? This is pretty much straight out of the RailsGuide.
EDIT & "fix"
Humph... apparently the Rails console was in a bad state. I restarted the console after the comments below and found that my query worked perfectly. Note that the quotes are required for this format of the WHERE clause and that statuses is a correct pluralization of status.
And after restarting, the where(status_date: date) format described by praga2050 worked, as did the where("statuses.status_date = ?", date) suggested by Kedarnag Mukanahallipatna.
Lesson learned: reload! doesn't reload changes in models; you have to restart the console for Model changes to be recognized. See this SO answer: https://stackoverflow.com/a/5428421/446464

I think you missed the point of naming conventions in rails.
Model class names are singular, and will map automatically to the plural database table name.
According to this, you can change your model name to statuses.
Or set a table name to use in the rails model:
class Status < ApplicationRecord
self.table_name = 'status'
scope :in_month, ->(date) { where(status_date: date) if date.present? }
end

I am not sure why you are putting string inside the Where clause
scope :in_month, ->(date){where("status_date = ?", date) if date.present?}
Should be change like below.
Below one should work for you.
scope :in_month, ->(date){where(status_date: date) if date.present?}

Related

Rails: weird behavior when getting last record while ordering

I'm using Rails 6 and I've noticed a strange behavior in Active Record when trying to get the latest record from a collection. Here is what I have:
session.rb
class Session < ApplicationRecord
has_many :participations
end
participation.rb
class Participation < ApplicationRecord
belongs_to :session
end
When I'm trying to get the latest participation with:
Participation.order(created_at: :desc).last
The SQL query generated looks like:
SELECT "participations".*
FROM "participations"
ORDER BY "participations"."created_at" ASC
LIMIT $1
Note that I did order(created_at: :desc) but the SQL is using ASC.
However, if I change my code to:
Participation.order(created_at: :asc).last
The SQL query is doing the opposite (a DESC):
SELECT "participations".*
FROM "participations"
ORDER BY "participations"."created_at" DESC
LIMIT $1
Does anyone have an explanation as to why it behave this way ? Is it a Rails bug ?
Seems like using last with order is causing this issue. If I remove last, ActiveRecord is generating the correct SQL (using the correct order)
ActiveRecord is optimizing the SQL statement for you. This
Participation.order(created_at: :desc).last
returns the same result as
Participation.order(created_at: :asc).first
But the latter statement is more efficient because it has to traverse fewer rows, so Rails generates SQL as if you had written it that way.

What does ActiveModel::MissingAttributeError in Rails mean in my case?

I have two Postgres tables that look like above.
amount_availables belongs to facilities as shown below:
class AmountAvailable < ApplicationRecord
belongs_to :sequence_number
belongs_to :facility
validates :facility, :presence => true
When I run a complex query that joins these 2 tables, I get the below error (and this error is not consistent):
ActiveModel::MissingAttributeError (missing attribute: facility_id):
Generated SQL:
This is the SQL that's generated:
SELECT "as_of_date", "entity", "facility", "financial_institution", "amount_availables"."amount_available", "amount_availables"."comments", "amount_availables"."last_updated_on", "amount_availables"."last_updated_by" FROM "amount_availables" INNER JOIN "sequence_numbers" ON "sequence_numbers"."sequence_number" = "amount_availables"."sequence_number_id" INNER JOIN "facilities" ON "facilities"."facility_id" = "amount_availables"."facility_id" WHERE (sequence_numbers.as_of_date >= '10/01/2019' and sequence_numbers.as_of_date <= '12/24/2019' AND facilities.entity in ('3C7','HOLD CO','PCM','PC-M','PFSI','PLS','PMIT','POP','QRS','TAG','TRS')) ORDER BY last_updated_on desc
NOTE:
And until last week, I remember I was getting this error inconsistently (it occurred a LOT of times but not ALL the time! But this week I seem to get it pretty much all the time. And this SQL runs just fine on Postgres client against the same database that my Rails app is using).
Does this error mean there should be a column named facility_id in facilities table?
What should I do on my Rails code to fix this?
I even tried renaming the id column in facilities table to facility_id using the Rails migration code below:
Approach 1 - Rails Migration Fix
class ModifyFacilitiesPkColumnName < ActiveRecord::Migration
def change
rename_column :facilities, :id, :facility_id
end
end
But I am still getting the same error even after the above approach (I DID run rake db:migrate and I can see in my postgres client that the id column in facilites HAS changed to facility_id).
What does this error mean precisely, and how do I fix this?
Your Inner Join seems to be incorrect:
SELECT * FROM "amount_availables"
INNER JOIN "sequence_numbers" ON "sequence_numbers"."sequence_number" = "amount_availables"."sequence_number_id"
INNER JOIN "facilities" ON "facilities"."facility_id" = "amount_availables"."facility_id"
WHERE (sequence_numbers.as_of_date >= '10/01/2019' and sequence_numbers.as_of_date <= '12/24/2019' AND facilities.entity in ('3C7','HOLD CO','PCM','PC-M','PFSI','PLS','PMIT','POP','QRS','TAG','TRS')) ORDER BY last_updated_on desc
Instead it should be:
INNER JOIN "facilities" ON "facilities"."id" = "amount_availables"."facility_id"
It should work.

Rails & Active Record query to find parent events without a child

An Event has_many Votes and a Vote belongs_to a User. How can I get a user's event's that they personally have not voted on? This was my best shot:
Event.includes(:votes).where.not("votes.user_id = ?", current_user.id)
But I am getting the below error:
PG::UndefinedTable: ERROR: missing FROM-clause entry for table
"votes"
You need to either use a hash within the final parenthesis or use references as per Active Record Query Interface. Here's the resulting code:
Event.includes(:votes).where.not("votes.user_id = ?", current_user.id).references(:votes)

How to order records by their latest child records attribute

I'm having troubles to order my records by their has_one association. I'm quite sure the solution is obvious, but I just can't get it.
class Migration
has_many :checks
has_one :latest_origin_check, -> { where(origin: true).order(at: :desc) }, class_name: 'Check'
end
class Check
belongs_to :migration
end
If I order by checks.status I always get different check ids. Shouldn't they be the same but with different order?
Or is the -> { } way to get the has_one association the problem?
Migration.all.includes(:latest_origin_check).order("checks.status DESC").each do |m| puts m.latest_origin_check.id end
So in one sentence: How do I order records through a custom has_one association?
I'm using Ruby 2.0.0, Rails 4.2 and PostgreSQL.
Update:
I wasn't specific enough. I've got two has_one relations on the checks relation.
Also very Important. One Migration has a way to big number of checks to include all the checks at once. So Migration.first.includes(:checks) would be very slow. We are talking about serveral thousand and I only need the latest.
class Migration
has_many :checks
has_one :latest_origin_check, -> { where(origin: true).order(at: :desc) }, class_name: 'Check'
has_one :latest_target_check, -> { where(origin: false).order(at: :desc) }, class_name: 'Check'
end
class Check
belongs_to :migration
end
Now if I get the latest_origin_check, I get the correct Record. The query is the following.
pry(main)> Migration.last.latest_origin_check
Migration Load (1.1ms) SELECT "migrations".* FROM "migrations" ORDER BY "migrations"."id" DESC LIMIT 1
Check Load (0.9ms) SELECT "checks".* FROM "checks" WHERE "checks"."migration_id" = $1 AND "checks"."origin" = 't' ORDER BY "checks"."at" DESC LIMIT 1 [["migration_id", 59]]
How do I get the latest check of each migration and then sort the migrations by a attribute of the latest check?
I'm using ransack. Ransack seems to get it right when I order the records by "checks.at"
SELECT "migrations".* FROM "migrations" LEFT OUTER JOIN "checks" ON "checks"."migration_id" = "migrations"."id" AND "checks"."origin" = 't' WHERE (beginning between '2015-02-22 23:00:00.000000' and '2015-02-23 22:59:59.000000' or ending between '2015-02-22 23:00:00.000000' and '2015-02-23 22:59:59.000000') ORDER BY "checks"."at" ASC
But the same query returns wrong results when I order by status
SELECT "migrations".* FROM "migrations" LEFT OUTER JOIN "checks" ON "checks"."migration_id" = "migrations"."id" AND "checks"."origin" = 't' WHERE (beginning between '2015-02-22 23:00:00.000000' and '2015-02-23 22:59:59.000000' or ending between '2015-02-22 23:00:00.000000' and '2015-02-23 22:59:59.000000') ORDER BY "checks"."status" ASC
Check.status is a boolean, check.at is a DateTime. A colleague suggested that the boolean is the problem. Do I need to convert the booleans to an integer to make them sortable? How do I do that only for the :latest_origin_check? Something like that?
.order("(case when \"checks\".\"status\" then 2 when \"checks\".\"status\" is null then 0 else 1 end) DESC")
You already have a has_many relationship with Check on Migration. I think you are looking for a scope instead:
scope :latest_origin_check, -> { includes(:checks).where(origin:true).order("checks.status DESC").limit(1)}
Drop the has_one :latest_origin_check line on Migration.
Migration.latest_origin_check
I think the line about should return your desired result set.

Ambiguous column name in Ruby on Rails with SQLite database?

I am getting this error in my Rails app:
ActiveRecord::StatementInvalid in PaymentsController#index
SQLite3::SQLException: ambiguous column name: date: SELECT COUNT(*) FROM "payments" INNER JOIN "invoices" ON "payments"."invoice_id" = "invoices"."id" WHERE "invoices"."user_id" = 1 AND (date >= '2013-01-01' and date <= '2013-12-31')
The problem seems to be that I have a date field in my invoices as well as my payments table.
Yet I still don't know how to fix this error.
class User < ActiveRecord::Base
def number_of_payments_in(year)
payments.where("payments.date >= ? and payments.date <= ?", "#{year}-01-01", "#{year}-12-31").count
end
end
class Payment < ActiveRecord::Base
def self.search(year)
if year
where("date >= ? and date <= ?", "#{year}-01-01", "#{year}-12-31")
end
end
end
Can anybody help?
This answer may be a bit vague as it's not clear from your question which class the self.search(year) method is in, but let me know if this doesn't help and we can try and go further.
My guess is that, unlike in the number_of_payments_in method, in the self.search method you haven't specified an appropriate table in the where call.
In number_of_payments_in, you specify payments.date, but in self.search you just use date. You said that you have a date field in your invoices as well as your payments table, so a join call across both tables will need every reference to date to be scoped by table.
Adding the appropriate table in front of date in self.search (as you have done in number_of_payments_in) may solve the problem.

Resources