My models:
OrderStatus
belongs_to Order
Order
has_one OrderStatus
belongs_to Logo
Logo
has_many Orders
I would like to perform a query on Logo model attribute named artwork:
OrderStatus.includes({:order => :logo}).where(:order => {:logo => {:artwork => search_artwork}})
but it basically throws an error:
SQLite3::SQLException: no such column: order.logo: SELECT COUNT(DISTINCT "order_statuses"."id") FROM "order_statuses" LEFT OUTER JOIN "orders" ON "orders"."id" = "order_statuses"."order_id" LEFT OUTER JOIN "logos" ON "logos"."id" = "orders"."logo_id" WHERE "order"."logo" = '---
:artwork: xxxxxxx'
I can't see the reason of this error.
EDIT
After extensive searching I realised that where part should be using table names (that is, plurals), so my code should be
#order_statuses = OrderStatus.includes(:order => [:logo]).where(:orders => {:logos => {:artwork => search_artwork}})
but I still see the SQLite3 exception error
SQLite3::SQLException: no such column: orders.logos: SELECT COUNT(DISTINCT "order_statuses"."id") FROM "order_statuses" LEFT OUTER JOIN "orders" ON "orders"."id" = "order_statuses"."order_id" LEFT OUTER JOIN "logos" ON "logos"."id" = "orders"."logo_id" WHERE "orders"."logos" = '---
:artwork: xxxxxxxx
'
So the quick answer to
I still see the SQLite3 exception error ... no such column: orders.logos"
Is that your orders table doesn't have a column named logos, it has a column named logo_id, which was created in your migration you mentioned in your comment.
The reason this error is happening is that Rails doesn't understand (as far as I know) nested values in a .where clause.
Perhaps more importantly, I think what you're (probably) more interested in is:
how do I write logic that will give me all the OrderStatus records related to the Logo with an artwork value equal to search_artwork?
If so, adding a has_one :through relationship to your Logo model will make this really easy!
OrderStatus
belongs_to Order
Order
has_one OrderStatus
belongs_to Logo
Logo
has_many Orders
has_many OrderStatuses, :through => :Order # This is what you're adding!
Then, your query should be as simple as:
Logo.where(artwork: search_artwork).includes(:orderStatus)
EDIT: oops the above is wrong
Logo.where(artwork: search_artwork).order_statuses
For more information on Rails relationships and using has_many :through, see:
http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
Related
I have a query which shows 3566 results what is ok.
When I use paginate on it, result is 18 but in console I see that query which it runs is ok
this is my controller
def listcontractors
#listcons = Contract.paginate(:page => params[:page], :per_page => 50).joins(:contractor)
.select("contractors.id,name,ico,city,country,count(resultinfo_id)")
.group("contractors.id,name,ico,city,country")
.order("name")
end
this is query I see in console, when I put it in psql result is ok
(22.2ms) SELECT COUNT(*) AS count_all,
contractors.id,name,ico,city,country AS
contractors_id_name_ico_city_country FROM "contractors" INNER JOIN
"contracts" ON "contracts"."contractor_id" = "contractors"."id" GROUP
BY contractors.id,name,ico,city,country Contractor Load (30.8ms)
SELECT contractors.id,name,ico,city,country,count(resultinfo_id) as
count FROM "contractors" INNER JOIN "contracts" ON
"contracts"."contractor_id" = "contractors"."id" GROUP BY
contractors.id,name,ico,city,country ORDER BY name LIMIT 50 OFFSET
1050
when I remove .paginate part from the query, result is ok
my models are
class Contract < ActiveRecord::Base
belongs_to :resultinfo
belongs_to :contractor
end
class Contractor < ActiveRecord::Base
has_many :contracts
end
I tried to switch query to Contractor.joins(:contract) but issue was same, with paginate result is much lower than it should be
any idea why this happens?
thanks
thanks to gmcnaughton I created this solution
ids = Contractor.order("name").pluck(:id)
#listcons = ids.paginate(:page => params[:page], :per_page => 50)
#groupedcons = Contractor.joins(:contracts)
.where(id: #listcons)
.select("contractors.id,name,ico,city,country,count(resultinfo_id)")
.group("contractors.id,name,ico,city,country")
.order("name")
and I had to add to initializers require 'will_paginate/array' because otherwise it shows undefined total_pages method for an array
Mixing paginate and group is tricky. paginate sets an OFFSET and LIMIT on the query, which get applied to the result of the GROUP BY -- rather than limiting what records will get grouped.
If you want to paginate through the all Contracts, then group each page of 50 results (one page at a time), try this:
def listcontractors
# get one page of contract ids
ids = Contract.paginate(:page => params[:page], :per_page => 50).pluck(:id)
# group just the items in that page
#listcons = Contract.where(id: ids)
.select("contractors.id,name,ico,city,country,count(resultinfo_id)")
.group("contractors.id,name,ico,city,country")
.order("name")
end
Hope that helps!
I have the following association:
Reservation
- has_many reservation_occupations
ReservationOccupations
- has_many reservation_occupants
ReservationOccupants
I want to do the following queries:
1 - Get the number of occupants for one reservation
2 - Get the number of occupants for a group of reservations (Reservations.all for example)
Thanks in advance!
1 - Get the number of occupants for one reservation
First, add a has_many :through association from Reservation to ReservationOccupant:
class Reservation < ActiveRecord::Base
has_many :reservation_occupations
has_many :reservation_occupants, through: :reservation_occupations
end
Now you can simply do
reservation = Reservation.first
reservation.reservation_occupants.count
2 - Get the number of occupants for a group of reservations
First, add some more associations:
class ReservationOccupant < ActiveRecord::Base
belongs_to :reservation_occupation
has_one :reservation, through: :reservation_occupation
end
and
class ReservationOccupation < ActiveRecord::Base
belongs_to :reservation
# ...
end
Then, to count the number of occupants for a group of reservations, you can add to your Reservation class the following method:
class Reservation < ActiveRecord::Base
# ...
def self.num_occupants(reservations)
ReservationOccupant
.joins(:reservation_occupation)
.joins(:reservation)
.where("reservations.id": reservations)
.count
end
end
It's worth noting that this num_occupants method works regardless of whether reservations is a collection of reservations or a single reservation. In other words, this method could be used for both of your questions, #1 and #2. However, the first method generates a more efficient SQL query, and is arguably a little clearer, so I'd personally use that when finding the number of occupants for a single reservation.
tldr;
In Rails, how do you set the target column of a foreign key to be a column other than its parent’s id?
Efforts so far
This feels like it should be a simple operation, but I’m having little success. I have a parent model, Order, which has many OrderItems, but I want the foreign key of OrderItems to reference a composite of Order’s reference1 and reference2 fields.
I’ve looked at a few paths:
First try
class Order < ActiveRecord::Base
has_many :order_items, foreign_key: :order_reference,
primary_key: :unique_reference
inverse_of: :order
validates :reference1, uniqueness: { scope: :reference2 }
end
class OrderItem < ActiveRecord::Base
belongs_to :order, foreign_key: unique_reference,
primary_key: order_reference
inverse_of: :order_item
end
(Where I created a redundant-feeling unique_reference column, that we populated before creation with reference1+reference2, and gave OrderItem a corresponding order_reference column)
I tried a few variants of the above, but couldn’t persuade the OrderItem to accept unique_reference as the key. It managed to link records, but then when I called OrderItem#order_reference, instead of contents matching the corresponding Order#unique_reference field it would return a stringified version of its parent’s id.
Second try
I removed the unique_reference column from the Order class, replacing it with a method of the same name and a has_many block:
class Order
has_many :order_items, -> (order) { where("order_items.order_reference = :unique_reference", unique_reference: order.unique_reference) }
def unique_reference
"#{reference1}#{reference2}"
end
end
class OrderItem < ActiveRecord::Base
belongs_to :order, ->(item){ where("CONCAT(surfdome_archived_order.source, surfdome_archived_order.order_number) = surfdome_archived_order_items.archived_order_reference") }
end
This time, calling Order#order_items raises a SQL error:
Unknown column 'order_items.order_id' in 'where clause': SELECT order_items.* FROM order_items WHERE order_items.order_id = 1 AND (order_items.order_reference = 'ref1ref2')
Every SQL query I’ve thought to try has the same underlying problem - somewhere, Rails decides we’re still implicitly trying to key by order_id, and I can’t find a way to persuade it otherwise.
Other options
At the moment my options seem to be to use the Composite Primary Keys gem or just ignoring Rails' built-in associations and hacking our own with db queries, but neither seems ideal - and it seems like Rails would surely have an option to switch foreign keys. But if so, where is it?
Thanks,
Sasha
I was hoping this would be simple, but in essence I would like to count the number of Users who have certain attributes through a join table in Rails 4.
I have a table called Views which holds three columns:
t.references :viewer
t.references :viewed
t.boolean :sentmessage, default: false
I then have the fields references as:
belongs_to :viewer, :class_name => "User"
belongs_to :viewed, :class_name => "User"
Each user record is then associated with a number of other records like Stats, Questions and a number of others. I'm interested in effectively counting how many viewers of a viewed record are Male or Female (and other search fields) which is data all held in User.stat.gender.name etc.
I'm trying to use a group statement but have no idea how to drill down and count the number of Males etc. I've tried:
#results = View.where(viewed: 63).group("viewer.stat.gender")
But this is so wrong it's frightening.
Any help to do this would be appreciated.
I worked it out finally. For anyone else who is interested:
View.where(viewed_id: 63).joins(viewer: {stat: :gender}).group("name").count
Didn't realise what an INNER JOIN was but some research and some trial and error means I can now show information about the users who have visited.
I have a recursive tree structure for handling my categories. Each leaf category can have zero or more deals. The categories is defined by
class Category < ActiveRecord::Base
has_many :sub_categories, class_name: "Category", foreign_key: "parent_category_id"
belongs_to :parent_category, class_name: "Category"
has_many :deal_categories
has_many :deals, through: :deal_categories
def leaf?
!has_sub_categories?
end
def has_sub_categories?
!sub_categories.empty?
end
end
Deals and DealCategories looks like follows:
class Deal < ActiveRecord::Base
has_many :deal_categories
has_many :categories, through: :deal_categories
end
class DealCategory < ActiveRecord::Base
belongs_to :deal
belongs_to :category
end
There are also some validations making sure that Deals only can exist as leaf categories. Thus, if I call category.deals on a leaf node I get some deals, and if I call it on a root node I get an empty result. All good.
But now I want category.deals to return the deals of it's children if it's not a root node. My approach has been to override the following method in my Category class as follows:
alias_method :original_deals, :deals
def deals
if leaf?
self.original_deals
else
self.sub_categories.deals
end
end
This however does not work as I can't call deals directly on sub_categories, the error being
undefined method `deals' for #<Category::ActiveRecord_Associations_CollectionProxy:0x00000009243d40>
How do I solve this?
You can't call deals on sub_categories because it is not a category...it is a collection of categories. Instead you could do something like
sub_categories.reduce([]) { |union, sub_category| union + sub_category.deals }
Using reduce creates a memo object (represented by the union variable) and the evaluation of the block becomes the new memo object. I am starting with an empty array and add in the result of calling deals on each sub_category in your sub_categories collection.