I would like to find a record from the records models associated models, associated model attribute.
I have a VendorOrder model and would like to find a VendorOrder (or vendor_orders, if matching criteria) by searching the vendor_order.order.shipments.each {|shipment| shipment.shipping_label.tracking_number}, if possible. So searching for a tracking number.
Models:
VendorOrder
belongs_to :order
has_many :shipments
Order
has_many :vendor_orders
has_many :shipments
has_many :shipping_labels
Shipment
belongs_to :order
belongs_to :vendor_order
belongs_to :shipping_label
ShippingLabel
belongs_to :order
has_one :shipment
I have in controller,
#vendor_orders = VendorOrder.joins(:order).merge(Order.where(order_status: "paid")).order(created_at: :desc).paginate(page: params[:vendor_page], per_page: 25)
if params[:search]
#vendor_orders = VendorOrder.search(params[:search])
else
#vendor_orders = VendorOrder.joins(:order).merge(Order.where(order_status: "paid")).order(created_at: :desc).paginate(page: params[:vendor_page], per_page: 25)
end
In VendorOrder model,
def self.search(search)
if search
find(:all, :conditions => ['shipments.map {|shipment| shipment.shipping_label.tracking_number} LIKE ?', "%#{search}%"])
else
find(:all)
end
end
View:
<%= form_tag vendor_orders_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
Error:
Couldn't find all VendorOrders with 'id': (all, {:conditions=>["shipments.map {|shipment| shipment.shipping_label.tracking_number} LIKE ?", "%99999999999999999999%"]}) (found 0 results, but was looking for 2).):
Obviously I am not fully understanding how this works. I have up until now only made search forms where the search within one model for a string.
How can I search through multiple layers of models but making sure a VendorOrder is the front model being searched for?
Update:
Using jvillian post:
Doing:
in model:
class << self
def find_by_tracking_number(tracking_number)
joins(:shipments).
where(shipments: {
id: Shipment.find_by(
shipping_label: ShippingLabel.where(tracking_number: tracking_number)
)
})
end
end
controller:
#vendor_orders = VendorOrder.joins(:order).merge(Order.where(order_status: "paid")).order(created_at: :desc).paginate(page: params[:vendor_page], per_page: 25)
if params[:search]
#vendor_orders = VendorOrder.find_by_tracking_number(params[:search])
else
#vendor_orders = VendorOrder.
joins(:order).
merge(Order.where(order_status: "paid")).
order(created_at: :desc).
paginate(page: params[:vendor_page], per_page: 25)
end
works to get things up and running but i run into the issue of no results.
Entering the numbers "99999999999999999999" should garner about 10 results but 0 appear.
on submit:
Parameters: {"utf8"=>"✓", "search"=>"99999999999999999999"}
does its magic:
SELECT "shipments".* FROM "shipments" WHERE "shipments"."shipping_label_id" IN (SELECT "shipping_labels"."id" FROM "shipping_labels" WHERE "shipping_labels"."tracking_number" = $1) LIMIT $2[0m [["tracking_number", "99999999999999999999"], ["LIMIT", 1]]
But then nothing after that, it just renders the page.
At this moment, there are about 10 VendorOrders that have shipments.map { |shpiment| shipment.shipping_label.tracking_number } that == "99999999999999999999"
Any reason why nothing is appearing? Missing something in my code to append the all results?
Give this a try:
VendorOrder.
joins(:shipments).
where(shipments: {
id: Shipment.find_by(shipping_label: ShippingLabel.find_by(tracking_number: #tracking_number))
})
To break it down, assume you have your tracking number in a variable called #tracking_number. This:
ShippingLabel.find_by(tracking_number: #tracking_number)
...will find your ShippingLabel. And this:
Shipment.find_by(shipping_label: ShippingLabel.find_by(tracking_number: #tracking_number))
...will find your Shipment that belongs_to the ShippingLabel.
Then, following the Specifying Conditions on the Joined Table section of the guide gives you the final query from way up above.
You might use this in the controller something like:
if params[:search]
#vendor_orders = VendorOrder.
joins(:shipments).
where(shipments: {
id: Shipment.find_by(
shipping_label: ShippingLabel.find_by(
tracking_number: params[:search]
)
)
})
else
#vendor_orders = VendorOrder.
joins(:order).
merge(Order.where(order_status: "paid")).
order(created_at: :desc).
paginate(page: params[:vendor_page], per_page: 25)
end
Or, you could put (some of) this in the VendorOrder model, something like:
class VendorOrder < ApplicationRecord
class << self
def find_by_tracking_number(tracking_number)
joins(:shipments).
where(shipments: {
id: Shipment.find_by(
shipping_label: ShippingLabel.find_by(tracking_number: tracking_number)
)
})
end
end
end
And then you might use it in a controller something like:
if params[:search]
#vendor_orders = VendorOrder.find_by_tracking_number(params[:search])
else
#vendor_orders = VendorOrder.
joins(:order).
merge(Order.where(order_status: "paid")).
order(created_at: :desc).
paginate(page: params[:vendor_page], per_page: 25)
end
That seems a little nicer, IMO, and hides some of the complexity from the controller.
Related
I would like to add has_many models LoanAmtPerSvcType to LoanContract array.
Below is my code, but it does not work.
When i check #contracts[0].loan_amt_per_svc_type.count , it return '0'
#members.each do |c|
#contracts << LoanContract.new(
:customer_id => c.id,
:season_id => #season.id,
:loan_type_id => #loan_type.id,
:cus_group_id => #group.id,
contract_date: #contract_date,
loan_duration: #loan_duration,
inspector_id: #inspector.id,
mgr_id: #manager.id,
user_id: #user.id)
end
#contracts.each do |lc|
lc.loan_amt_per_svc_type = [LoanAmtPerSvcType.new(customer_service_type_id: 1), LoanAmtPerSvcType.new(customer_service_type_id: 2)]
end
render :text => #contracts[0].loan_amt_per_svc_type.count
#contracts[0].loan_amt_per_svc_type.count return 0, because you didn't save your contracts into the database.
You can use LoanContract.create instead of LoanContract.new. Also with associations.
If you only want to know count of loan_amt_per_svc_type use size method.
#contracts[0].loan_amt_per_svc_type.size
As i'm trying to show a specific data from has_many relation on a iteration
Why Rails shows undefined method for Null:String and how do I fix it?
NoMethodError (undefined method `rated' for "shop_date is not
null":String)
# controller Cast
def cast
#products = Product.all
#rating = Product.joins(:shops).where(:shops => ('shop_date is not null').rated.average(:b_rating) || 0 )
end
# Model Shop
belongs_to :product
attr_accessible :b_rating,:product_id
scope :by_participant, -> { where('shop_date is not null').order('shop_date desc') }
scope :rated, -> { where('b_rating is not null') }
def self.average_rating
by_participant.rated.average(:b_rating) || 0
end
# Model Product
Has_many :shops
I suppose, simplest way is to user group by query:
# controller
#products = Product.all
products_ids = #products.collect(&:id)
#ratings =
Shop.where(product_id: products_ids).
where('shop_date is not null').rated.
group(:product_id).
average(:b_rating)
# view
<% #products.each do |product| %>
Rating: <%= #ratings[product.id] || 0 %>
<% end %>
I have a Rails 5 app and I'm trying to do an aggregations search for a has_and_belongs_to_many association.
Here is the code that I have so far:
event.rb:
class Event < ApplicationRecord
searchkick text_start: [:title]
has_and_belongs_to_many :services
has_and_belongs_to_many :sectors
def search_data
atributes.merge(
title: title,
description: description,
sector_name: sectors.map(&:name),
service_name: services.map(&:name)
)
end
end
events_controller.rb:
def index
query = params[:j].presence || "*"
conditions = {}
conditions[:sector] = params[:sector] if params[:sector].present?
conditions[:service] = params[:service] if params[:service].present?
conditions[:date] = params[:date] if params[:date].present?
#events = Event.search query, where: conditions, aggs: [:sector, :service, :date], order: {created_at: {order: "desc"}}, page: params[:page], per_page: 10
end
When I call Event.reindex in the console I was expecting to to show that the sectors and services had been indexed but it doesn't work.
To be honest I'm getting quite lost and going round in circles so any help would be much appreciated.
This is the code that ended up working for me:
event.rb:
def index
query = params[:j].presence || "*"
conditions = {start_date: {"gte": "now/d"}}
conditions[:sector_name] = params[:sector_name] if params[:sector_name].present?
conditions[:service_name] = params[:service_name] if params[:service_name].present?
conditions[:start_date] = params[:start_date] if params[:start_date].present?
#events = Event.search query, where: conditions, aggs: [:sector_name, :service_name], order: {start_date: {order: "asc", unmapped_type: "long"}}, page: params[:page], per_page: 10
end
events_controller.rb:
def search_data
{
title: title,
location: location,
description: description,
start_date: start_date,
sector_name: sectors.map(&:name),
service_name: services.map(&:name)
}
end
i am trying to delete from list but when i am trying this it is getting deleted from database
#course = Course.find(params[:id])
#search = Lesson.search(params[:q])
#lessons = #search.result.paginate(:page => params[:page], :per_page => 10)
#search.build_condition if #search.conditions.empty?
#course.lessons.each do |lesson|
#lessons.each do |l|
if lesson.id == l.id
#lessons.delete(l)
end
end
end
I am getting this error: delete_all doesn't support limit scope
Thanking you
Delete is an ActiveRecord method. I assume you don't want to delete it from the database but from the result list. You can do it like this:
#course.lessons.each do |lesson|
#lesson.reject { |l| l.id == lesson.id }
end
I have Awards and Categories, joined with Awards_Categories
I have 2 Categories.
I need to find all the Awards that have Category.id = 1, but not Category.id =2. Some categories have one or the other, some have just one. I want a list of the Awards that have category 1 but not category 2.
I have a scope:
scope :in_categories, lambda { |categories|
joins(:categories).
where(:awards_categories => { :category_id => categories } ).
select("DISTINCT awards.*")
}
And this works with a query like:
#awardsall = Award.in_categories([1 && 2]).order("name ASC")
I have tried
#awards_store = Award.in_categories([1]).order("name ASC")
With:
<% #awards_store.each do |store| %>
<li><%= link_to store.name, award_path(store), :title => store.info %> |
<% store.categories.each do |cat| %>
<%= cat.id%>
<% end %>
</li>
<% end %>
EDIT---
I know the block is not what I need. it is just my attempt at finding a way to make it work.
And while this lists all the awards, and all the award categories its still grabbing awards that have category.id = 2 because some awards have both
any ideas?
Sorry, didn't test it, but the main idea is to count the rows in the connecting table.
scope :in_categories, lambda { |*categories|
joins(:categories).
where(:awards_categories => { :category_id => categories } ).
where("(select count(distinct category_id) from awards_categories where category_id in (?)) = ?", categories, categories.size)
}
and use it this way:
#awardsall = Award.in_categories(1, 2).order("name ASC")
#awards_store = Award.in_categories(1).order("name ASC")
If you have a model for awards_categories, then it will look better:
scope :in_categories, lambda { |*categories|
joins(:categories).
where(:awards_categories => { :category_id => categories } ).
where("#{AwardCategory.where(:category_id => categories).count("distinct category_id").to_sql}=#{categories.size}")
}