how append an object to association relation in rails? - ruby-on-rails

In a rails 4.1 application I need to add an object to an "AssociationRelation"
def index
employee = Employee.where(id_person: params[:id_person]).take
receipts_t = employee.receipts.where(:consent => true) #gives 3 results
receipts_n = employee.receipts.where(:consent => nil).limit(1) #gives 1 result
#I would need to add the null consent query result to the true consent results
#something similar to this and the result is still an association relation
#receipts = receipts_t + receipts_n
end
Is there a simple way to do this?

A way of solving this:
def index
employee_receipts = Employee.find_by(id_person: params[:id_person]).receipts
receipts_t = employee_receipts.where(consent: true)
receipts_n = employee_receipts.where(consent: nil).limit(1)
#receipts = Receipt.where(id: receipts_t.ids + receipts_n.ids)
end
Unfortunately .or() can't be used here because it's only available from Rails v5.0.0.1

you could do this way
receipts_t_ids = employee.receipts.where(:consent => true).pluck(:id)
receipts_n_ids = employee.receipts.where(:consent => nil).limit(1).pluck(:id)
#receipts = Receipt.where(id: receipts_t_ids + receipts_n_ids)

To avoid extra queries and keeping arrays in memory, you can use or
Like this:
def index
employee_receipts = Employee.find_by(id_person: params[:id_person]).receipts
#receipts =
employee_receipts.where(consent: true).or(
employee_receipts.where(consent: nil).limit(1)
)
end

Related

Chaining multiple ActiveRecord `or` queries

I've got an array of columns that I want to loop through and optionally chain an or query onto an ActiveRecord query chain. I can get it to work, but the resulting query appends the or onto the query chain, therefore making the columns in my inital query optional. Here's my class:
class Claim
class MatchingAttributeFinder
ATTRIBUTE_GROUPS_TO_MATCH = [
["teacher_reference_number"],
["email_address"],
["national_insurance_number"],
["bank_account_number", "bank_sort_code", "building_society_roll_number"],
].freeze
def initialize(source_claim, claims_to_compare = Claim.submitted)
#source_claim = source_claim
#claims_to_compare = claims_to_compare
end
def matching_claims
claims = #claims_to_compare.where.not(id: #source_claim.id)
ATTRIBUTE_GROUPS_TO_MATCH.each do |attributes|
vals = values_for_attributes(attributes)
next if vals.blank?
concatenated_columns = "CONCAT(#{attributes.join(",")})"
claims = claims.or(
Claim.where("LOWER(#{concatenated_columns}) = LOWER(?)", vals.join)
)
end
claims
end
private
def values_for_attributes(attributes)
attributes.map { |attribute|
#source_claim.read_attribute(attribute)
}.reject(&:blank?)
end
end
end
The generated SQL looks like this:
SELECT "claims".* FROM "claims" WHERE (((("claims"."submitted_at" IS NOT NULL AND "claims"."id" != 'a7b25b99-4477-42b1-96ab-8262582c5541' OR (LOWER(CONCAT(teacher_reference_number)) = LOWER('0902344'))) OR (LOWER(CONCAT(email_address)) = LOWER('genghis.khan#mongol-empire.com'))) OR (LOWER(CONCAT(national_insurance_number)) = LOWER('QQ891011C'))) OR (LOWER(CONCAT(bank_account_number,bank_sort_code,building_society_roll_number)) = LOWER('34682151972654123456789/ABCD')))
But what I actually want is more like this:
SELECT "claims".* FROM "claims" WHERE "claims"."submitted_at" IS NOT NULL AND "claims"."id" != 'd6a53b4d-c569-49e6-a2ea-ac44b69b0451' AND (LOWER(concat(teacher_reference_number)) = LOWER('0902344') OR LOWER(concat(email_address)) = LOWER('genghis.khan#mongol-empire.com') OR LOWER(concat(national_insurance_number)) = LOWER('QQ891011C') OR LOWER(concat(bank_account_number,bank_sort_code,building_society_roll_number)) = LOWER('34682151972654123456789/ABCD'))
Is there any way to set up something like an empty scope that I can chain my OR queries to?
Try chaning all the "or" together first and then chain the original query
def matching_claims
claims = #claims_to_compare.where.not(id: #source_claim.id)
ors = nil
ATTRIBUTE_GROUPS_TO_MATCH.each do |attributes|
vals = values_for_attributes(attributes)
next if vals.blank?
concatenated_columns = "CONCAT(#{attributes.join(",")})"
aux = Claim.where("LOWER(#{concatenated_columns}) = LOWER(?)", vals.join)
if ors.nil?
ors = aux
else
ors = ors.or(aux)
end
end
claims.merge(ors)
end

Trouble adding "and" and "or" clauses in query through rails query interface

I am trying to run following query through Rails query interface but unable to translate my logic. The query is
Select f.* from feeds f
Left join feed_items fi on fi.id = f.feedable_id
where
f.feedable_type in ('Homework', 'Datesheet')
and
(
(fi.assignable_type = 'Level' and assignable_id IN (1)) or
(fi.assignable_type = 'Student' and assignable_id IN (1)) or
(fi.assignable_type = 'Section' and assignable_id IN (1))
)
Scenario:
I receive following params hash in my action containing filters which will be added dynamically in my query
{"page"=>"1", "limit"=>"2", "type_filter"=>["Homework", "Datesheet"], "assignable_filter"=>{"Student"=>"[2]", "Section"=>"[1]", "Level"=>"[1]"}}
So far, what I have done is joining the tables and added where clause for type filter but not sure how to dynamically add assignable_filters. Here is my rails code, options are params in following code
def get_feeds(options)
base = Feed.includes(:feed_item)
base = add_type_filters base, options
base = add_assignable_filters base, options
format_response base, options
end
def add_type_filters(base, options)
type_filter = options[:type_filter]
if !type_filter.nil? and type_filter.length > 0
base = base.where('feedable_type IN (?)', options[:type_filter])
end
base
end
def add_assignable_filters(base, options)
assignable_filter = options[:assignable_filter]
if !assignable_filter.nil?
assignable_filter.each do |key, value|
# code for adding filters combined with or conditions
end
# wrap the or conditions and join them with an and in main where clause
end
base
end
P.S I am using rails 5
There was no straight forward way of building the query dynamically. I had to construct the where string to solve the problem. My current solution is
def get_feeds(options)
params_hash = {}
type_filters = add_type_filters options, params_hash
assignable_filters = add_assignable_filters options, params_hash
where = type_filters
where = where ? "#{where} and (#{assignable_filters})" : assignable_filters
base = Feed.eager_load(:feed_item).where(where, params_hash)
format_response base, options
end
def add_type_filters(options, params_hash)
type_filter = options[:type_filter]
type_filter_sql = nil
if !type_filter.nil? and type_filter.length > 0
type_filter_sql = 'feeds.feedable_type in (:type_filter)'
params_hash[:type_filter] = type_filter
end
type_filter_sql
end
def add_assignable_filters(options, params_hash)
assignable_filter_sql = []
assignable_filter = options[:assignable_filter]
if !assignable_filter.nil?
assignable_filter.each do |key, value|
assignable_filter_sql.push("(feed_items.assignable_type = '#{key}' and feed_items.assignable_id IN (:#{key}))")
params_hash[key.to_sym] = JSON.parse(value)
end
end
assignable_filter_sql.join(' or ')
end

How can I iterate through a model then iterate again in my view?

I want to pull data for each of my users. I grab their person_id from my user table, then use each person's ID to figure out how many days each person has available, and show that in my view.
I'm not sure if I am doing this correctly because I am iterating in my controller then again in my view.
def how_many_days_users_have
#my_group = User.all.pluck(:person_id)
#my_group.each do |v|
#indirect_id_v = Empaccrl.where("person_id = ? and is_active = ?", '#{v]', 'Y').pluck(:a_code).first
#v_range = Empaccrl.where("person_id = ? and is_active = ?", '#{v]', 'Y').pluck(:ac).first
#v_range_taken = Empaccrl.where("person_id = ? and is_active = ?", '#{v]', 'Y').pluck(:taken).first
#total_v_hours = #v_range.to_d - #v_range_taken.to_d
#total_v_days = #total_v_hours / 8
end
Then in my view I use this to show me this data:
%tr.trace-table
-#indirect_id_v.each do |idd|
%tr.trace-table
%td.trace-table{:style => 'border: solid black;'}= idd
-#total_v_days.each do |days|
%tr.trace-table
%td.trace-table{:style => 'border: solid black;'}= days
Okay, first things first, move some of that junk to your model, like so:
class Empaccrl < ActiveRecord::Base
def self.all_people
where(person_id: User.all.pluck(:person_id))
end
def self.active_people
all_people.where(is_active: 'Y')
end
def self.active_vacation_data
active_people.select(:person_id, :ac, :taken)
end
def total_v_hours
ac.to_d - taken.to_d
end
def total_v_days
total_v_hours / 8
end
end
Then you can use:
peoples_vacation_information = Empaccrl.active_vacation_data.all
peoples_vacation_information.map do |person|
p "person #{person.person_id} has #{person.total_v_days} vacation days"
end
Honestly, you don't even need all that, but I'm not sure why you are doing what you are doing, so I figured better be safe and add stuff. Whatever you don't need, just ignore.

Add extra filter parameters in RoR

Suppose I have a method in a controller:
def my_find(is_published, count)
items = Idea.where(published: is_published)
#......
end
Sometimes I want to pass some extra filter arguments
def my_find(is_published, count, some_extra_filter = nil)
items = Idea.where(published: is_published) #.where (some_extra_filter)
#......
end
where some_extra_filter can be lambda or just an plain sql "where" string and it can also be nil or "".
So how do I concatenate .where(published: is_published) with where (some_extra_filter) to get what I need?
This is actually very easy using scopes:
def my_find
#items = Idea.scoped
#items = #items.where(published: is_published) unless is_published.nil?
#items = #items.where(other: other_param) if other_params < 10
# etc, etc
end

Ruby loop problems

I am working on a script that is supposed to be writing a list of items to a hash, but for some reason its only placing the last item in the loop in the hash... I have been working on this script all day, so I am pretty sure its something I am just missing.
Here is the script
#mr = MediaRating.where("user_id = ?", session['user_credentials_id'])
#mr.each do |rating|
#m = Media.where("id = ?", rating.media_id)
#m.each do |m|
s = Profile.find_by_subscriber_id(m.subscriber_id)
#h_lang = Language.find_by_code(s.language)
#history = {m.title => #h_lang.english}
end
end
There are multiple records in the MediaRating table so I know it has to do something with how my loop is. Thanks in advance for the help!
Working code:
#mr = MediaRating.where("user_id = ?", session['user_credentials_id'])
#mr.each do |rating|
#m = Media.find(rating.media_id)
s = Profile.find_by_subscriber_id(#m.subscriber_id)
#h_lang = Language.find_by_code(s.language)
#history[#m.title] = #h_lang.english
end
In the last line, you are over-writing the entire #history hash instead of adding a new key/value pair to it. I'm guessing that isn't what you intended. Change this line:
#history = {m.title => #h_lang.english}
to this:
#history[m.title] = #h_lang.english

Resources