I have a model
class Order < ActiveRecord::Base
...
enum status: [:started, :finished, :failed, :processing]
...
end
And want to select all processing and finished orders. I cannot write simply
#orders = Order.where(status: [:finished, :processing])
because status field is naturally an integer, not string or literal. So this statement generates
2.2.0 :008 > Order.where(status: [:finished, :processing])
SELECT "orders".* FROM "orders" WHERE "orders"."status" IN (NULL, NULL)
Now i do the following
#orders = Order.where(status: [:finished, :processing].map { |s| Order.statuses[s] }
Is there any better way?
As you want to call order statuses by it's names, the below should work
#orders = Order.where(status: Order.statuses[:finished, :processing])
Related
I wrote a below query class, which is throwing a missing attribute error which I didn't put in the select method anywhere. Any idea from where this error might be coming? Why project_id is being searched in the to_json method.
class WorkitemPriceResearchPresenter
def initialize(company)
#company = company
end
def query
#workitems ||= \
#company
.workitems
.joins(
:workitem_category,
:workitem_status,
{ project: [:shipyard, {vessel: :vessel_category}] },
)
.select <<-EOS
workitems.id as id,
projects.sequential_id as sequential_id,
vessels.name as vessel_name,
vessel_categories.code as vessel_category,
shipyards.name as shipyard,
workitems.item_code as item_code,
workitems.description as description,
workitems.unit as unit,
workitems.price_cents as price_cents,
workitems.currency as currency,
workitems.quantity as quantity,
workitem_statuses.name as status,
workitems.discount as discount,
DATE_PART('year', projects.scheduled_from::date) as scheduled_from_year,
projects.scheduled_from as scheduled_from,
DATE_PART('year', projects.scheduled_to::date) as scheduled_to_year,
projects.scheduled_to as scheduled_to,
workitem_categories.name as Category
EOS
end
def as_json
query.to_json
end
end
Error is:
pry(main)> WorkitemPriceResearchPresenter.new(company).query.as_json
Workitem Load (33.7ms) SELECT workitems.id as id,
projects.sequential_id as sequential_id,
vessels.name as vessel_name,
vessel_categories.code as vessel_category,
shipyards.name as shipyard,
workitems.item_code as item_code,
workitems.description as description,
workitems.unit as unit,
workitems.price_cents as price_cents,
workitems.currency as currency,
workitems.quantity as quantity,
workitem_statuses.name as status,
workitems.discount as discount,
DATE_PART('year', projects.scheduled_from::date) as scheduled_from_year,
projects.scheduled_from as scheduled_from,
DATE_PART('year', projects.scheduled_to::date) as scheduled_to_year,
projects.scheduled_to as scheduled_to,
workitem_categories.name as Category
FROM "workitems" INNER JOIN "workitem_categories" ON "workitem_categories"."id" = "workitems"."workitem_category_id" INNER JOIN "workitem_statuses" ON "workitem_statuses"."id" = "workitems"."workitem_status_id" INNER JOIN "projects" ON "projects"."id" = "workitems"."project_id" AND "projects"."deleted_at" IS NULL INNER JOIN "shipyards" ON "shipyards"."id" = "projects"."shipyard_id" AND "shipyards"."deleted_at" IS NULL INNER JOIN "vessels" ON "vessels"."id" = "projects"."vessel_id" AND "vessels"."deleted_at" IS NULL INNER JOIN "vessel_categories" ON "vessel_categories"."id" = "vessels"."vessel_category_id" AND "vessel_categories"."deleted_at" IS NULL WHERE "workitems"."deleted_at" IS NULL AND "workitems"."company_id" = $1 [["company_id", "c61e4368-1a60-464b-8002-9da31bf301e5"]]
ActiveModel::MissingAttributeError: missing attribute: project_id
from /Users/ar/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-4.2.11.3/lib/active_record/attribute_methods/read.rb:93:in `block in _read_attribute'
Let me know if I need to provide any more information.
Since you are joining workitems with project, your workitems is supposed to have a project_id attribute in order to join the tables. But it looks like you do not have such column, so you are getting this error.
Model Food has scope expired:
Food.rb
class Food < ApplicationRecord
default_scope { where.not(status: 'DELETED') }
scope :expired, -> { where('exp_date <= ?', DateTime.now) }
belongs_to :user
end
In my controller I'm chaining where conditions to filter foods by user and status:
query_type.rb
def my_listing_connection(filter)
user = context[:current_user]
scope = Food.where(user_id: user.id)
if filter[:status] == 'ARCHIVED'
# Line 149
scope = scope.where(
Food.expired.or(Food.where(status: 'COMPLETED'))
)
else
scope = scope.where(status: filter[:status])
end
scope.order(created_at: :desc, id: :desc)
# LINE 157
scope
end
Here is the rails log:
Food Load (2.7ms) SELECT `foods`.* FROM `foods` WHERE `foods`.`status` !=
'DELETED'
AND ((exp_date <= '2020-07-02 09:58:16.435609') OR `foods`.`status` = 'COMPLETED')
↳ app/graphql/types/query_type.rb:149
Food Load (1.6ms) SELECT `foods`.* FROM `foods` WHERE `foods`.`status` != 'DELETED'
AND `foods`.`user_id` = 1 ORDER BY `foods`.`created_at` DESC, `foods`.`id` DESC
↳ app/graphql/types/query_type.rb:157
Why does active records query loses expired scope (and a condition) in line 157?
It is ignored because where doesn't expect scopes like that. But you can use merge instead. Replace
scope = scope.where(
Food.expired.or(Food.where(status: 'COMPLETED'))
)
with
scope = scope.merge(Food.expired)
.or(Food.where(status: 'COMPLETED'))
or
scope = scope.where(status: 'COMPLETED').or(Food.expired)
I am listing products and I want to be able to pass a hash as my where clause so I can do something like:
filter = {}
filter[:category_id] = #category.id
filter[:is_active] = true
#products = Products.where(filter)
Is it possible to do this somehow?
I also need to add something like this in my where clause:
WHERE price > 100
How could I add that to a filter?
The reason I want to do this is because in the UI I will have a set of optional filters, so then I will use if clauses in my controller to set each filter.
You can pass a hash to where exactly like you did:
filter = {
category_id: #category_id,
is_active: true
}
#products = Product.where(filter)
Using a hash only works for equality (e.g. category_id = 123), so you can't put something like price > 100 in there. To add that criteria, just add another where to the chain:
#product = Product.where(filter).where('price > 100')
Or...
#product = Product.where(filter)
if params[:min_price]
#product = #product.where('price > ?', min_price)
end
You could have a bit of fun with scopes: write a scope that's actually a mini predicate builder, sanitizing and pattern-matching strings, and delegating to the standard predicate builder for other scalar types. E.g.
# app/models/concerns/searchable.rb
module Searchable
extend ActiveSupport::Concern
included do
scope :search, ->(params) {
params.inject(self) do |rel, (key, value)|
next rel if value.blank?
case value
when String
rel.where arel_table[key].matches '%%%s%%' % sanitize_sql_like(value)
when Range, Numeric, TrueClass, FalseClass
rel.where key => value
else
raise ArgumentError, "unacceptable search type"
end
end
}
end
end
# app/models/product.rb
class Product < ApplicationRecord
include Searchable
then you can
filter = { name: 'cheese', description: 'aged', age: 42.. }
Product.search(filter) #=> SELECT "products".* FROM products WHERE "products"."name" ILIKE '%cheese%' AND "products"."description" ILIKE '%aged%' AND "products"."age" >= 42
I am following a railscast episode but I'm using postgresql and am getting a group_by error:
PG::Error: ERROR: column "ratings.created_at" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT created_at, sum(score) as total_score FROM "ratings" ...
^
: SELECT created_at, sum(score) as total_score FROM "ratings" WHERE ("ratings"."created_at" BETWEEN '2014-01-02 00:00:00.000000' AND '2014-01-23 13:43:06.187741') GROUP BY date(created_at)
How should I modify my code below to include group_by created_at
def self.chart_data(start = 3.weeks.ago)
total_votes = votes_by_day(start)
(start.to_date..Date.today).map do |date|
{
created_at: date,
total_score: total_prices[date] || 0
}
end
end
def self.votes_by_day(start)
ratings = where(created_at: start.beginning_of_day..Time.zone.now)
ratings = ratings.group("date(created_at)")
ratings = ratings.select("created_at, sum(score) as total_score")
ratings.each_with_object({}) do |rating, scores|
scores[rating.created_at.to_date] = rating.total_score
end
end
Your group by clause and your select clause have different attributes. If you're grouping by "date(created_at)" then you can no longer select "created_at."
def self.votes_by_day(start)
ratings = where(created_at: start.beginning_of_day..Time.zone.now)
ratings = ratings.group("date(created_at)")
ratings = ratings.select("date(created_at), sum(score) as total_score")
ratings.each_with_object({}) do |rating, scores|
scores[rating.created_at.to_date] = rating.total_score
end
This following is my model, For easier reading, I cut some code:
class Meeting < ActiveRecord::Base
def Joiners
# Never mind , this is a complex sql, i think you do not need to read it.
Joiners = Ploy.connection.select_all("SELECT count(users.id) as joiner FROM `ploys` INNER JOIN `participants` ON `participants`.`ploy_id` = `ploys`.`id` INNER JOIN `users` ON `users`.`id` = `participants`.`user_id` where `participants`.`ploy_id`= #{self.id}")
Joiners.rows[0][0]
end
def as_json(options={})
super(methods: :Joiners)
end
If i use
render json: #Meeting
It will render a JSON with Joiners attribute .
{
"Meeting":{
...
"Joiners":15,
...
}
"Meeting":{
...
"Joiners":13,
...
}
}
So:
How can i sort by Joiners ?
Thanks
You'll have to order it in a query result by using SQL ORDER BY