I'm getting a somewhat cryptic error message of
ActiveRecord::ConfigurationError 1
when trying to execute #invoices = invoice.joins(pos: [vendor_id: 1])
I am trying to call all invoices that belong to POs that belong to Vendor 1.
My models are setup as follows:
Vendors can have many POs, and POs can have many invoices
class Vendor < ApplicationRecord
has_many :pos
class Po < ApplicationRecord
belongs_to :vendor
has_many :items, :dependent => :destroy
has_many :invoices
class Invoice < ApplicationRecord
belongs_to :po
I gather that the ActiveRecord::ConfigurationError is
Raised when association is being configured improperly or user tries
to use offset and limit together with ActiveRecord::Base.has_many or
ActiveRecord::Base.has_and_belongs_to_many associations.
But I'm having trouble figuring out what's wrong with my associations. Any ideas?
I think your associations are OK... I think your statement is just bad, and usually when I need to query like this, I resort to a little SQL clause because it's either too complex or flat-out hard to read otherwise.
Try this (note the capital I in Invoice):
vendor_id = 1
#invoices = Invoice.joins(:po).where("pos.vendor_id = ?", vendor_id)
Related
I have the following model setup in a Rails 5.0.0.1 app on Heroku with Postgres 9.5.5:
class Tape < ApplicationRecord
has_many :numbers
belongs_to :player
end
class Player < ApplicationRecord
has_many :tapes
has_many :snapshots
end
class Number < ApplicationRecord
belongs_to :tape
belongs_to :snapshot
end
class Snapshot < ApplicationRecord
has_many :numbers, dependent: :destroy
belongs_to :player
end
#I want to order #numbers by the created_at field of its other parent, Snapshot, not tape
#tape = Tape.includes(player: {snapshots: :numbers}).find 55
#numbers = Number.includes(snapshot: :player).where(players: {id: #tape.player_id}, tape_id: #tape.id).order('snapshots.created_at DESC')
It seems like I'm having to do extra SQL work simply to order #numbers by its parent Snapshot.
I tried #tape.numbers.joins(:snapshot).order('snapshots.created_at DESC') and I see a SQL query ending in something like '....ORDER BY "numbers"."id" ASC, snapshots.created_at DESC' which means Rails interprets this to mean I want to order by numbers.id as well - which I don't. I only want to order by snapshots.created_at. Now a query like #tape.numbers.joins... will also be an extra SQL query. 2 questions:
1) what's the code I can write that will result in the least amount of SQL work? Or is what I have pretty much it?
2) Why does rails insert the extra '....ORDER BY "numbers"."id" clause in #tape.numbers.joins(:snapshot).order('snapshots.created_at DESC')?
I have a few models...
class Game < ActiveRecord::Base
belongs_to :manager, class_name: 'User'
has_many :votes
end
class Vote < ActiveRecord::Base
belongs_to :game
belongs_to :voter, class_name: 'User'
end
class User < ActiveRecord::Base
has_many :games, dependent: :destroy
has_many :votes, dependent: :destroy
end
In my controller, I have the following code...
user = User.find(params[:userId])
games = Game.includes(:manager, :votes)
I would like to add an attribute/method voted_on_by_user to game that takes a user_id parameter and returns true/false. I'm relatively new to Rails and Ruby in general so I haven't been able to come up with a clean way of accomplishing this. Ideally I'd like to avoid the N+1 queries problem of just adding something like this on my Game model...
def voted_on_by_user(user)
votes.where(voter: user).exists?
end
but I'm not savvy enough with Ruby/Rails to figure out a way to do it with just one database roundtrip. Any suggestions?
Some things I've tried/researched
Specifying conditions on Eager Loaded Associations
I'm not sure how to specify this or give the includes a different name like voted_on_by_user. This doesn't give me what I want...
Game.includes(:manager, :votes).includes(:votes).where(votes: {voter: user})
Getting clever with joins. So maybe something like...
Game.includes(:manager, :votes).joins("as voted_on_by_user LEFT OUTER JOIN votes ON votes.voter_id = #{userId}")
Since you are already includeing votes, you can just count votes using non-db operations: game.votes.select{|vote| vote.user_id == user_id}.present? does not perform any additional queries if votes is preloaded.
If you necessarily want to put the field in the query, you might try to do a LEFT JOIN and a GROUP BY in a very similar vein to your second idea (though you omitted game_id from the joins):
Game.includes(:manager, :votes).joins("LEFT OUTER JOIN votes ON votes.voter_id = #{userId} AND votes.game_id = games.id").group("games.id").select("games.*, count(votes.id) > 0 as voted_on_by_user")
I have tables called users, orders, and delivery_times that are linked using the following relationship.
For table User:
belongs_to :orders
For table orders:
belongs_to :delivery_times
I want to write a query on table users using a condition on table delivery_times as shown:
User.includes(order: :delivery_time).where("delivery_times.start < ?",Time.now)
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "delivery_times"
However I get an error. Can I use the RoR ORM to make this query work using includes, even though I know there is a solution using joins?
You will need a join for this kind of query, since you need the joint knowledge of the delivery_times table and the users table.
What includes actually does is it decides between preload and eager_load automatically and tries to always take the better one. In you case it will do an eager_load; have a look into this article.
For the error you get, I guess it yould result from starting with Users and not User:
User.includes(order: :delivery_time).where("delivery_times.start < ?",Time.now)
Everything else seems correct to me.
The better definition of relations between your models would be this:
So your classes would look like this:
class User < ActiveRecord::Base
has_many :orders
end
class Order < ActiveRecord::Base
belongs_to :user
has_one :delivery_time
end
class DeliveryTime < ActiveRecord::Base
belongs_to :order
end
The query you are making doesn't make any sense? What is the result that you are expecting?
If you want to get order that their delivery time is a specific time you can use scopes:
class Order < ActiveRecord::Base
belongs_to :user
has_one :delivery_time
scope :ready_to_deliver, includes(:delivery_time).where("delivery_time.start < ? ", Time.now)
end
Then you can get orders that are ready to deliver like this:
ready_orders = Order.ready_to_deliver
Not new to Ruby on Rails, but never really worked with more complicated ActiveRecord queries.
Say I have a Affiliate model that has_many referred users and referred users has_many purchased_products.
What I want to do is an efficient ActiveRecord way of getting the total sum of the count of purchased_products of all the referred users. How do I go about doing this?
Thanks.
Assuming objects like:
class Affiliate < ActiveRecord::Base
has_many :users
end
class Users < ActiveRecord::Base
#should have purchased_products_count integer column
belongs_to :affiliate
has_many :pruchased_products
end
class PurchasedProducts < ActiveRecord::Base
belongs_to :user, counter_cache: :purchased_products_count
end
products_count = User.first.purchased_products.size # uses counter_cache to get the size
another_products_count = User.first.purchased_products_count # get the value diretly
all_users_products_count = my_affiliate.users.map(&:purchased_products_count).inject(:+) # makes an array of product counts then sums them
I think this might also work
my_affiliate.users.sum('purchased_products_count')
I have 3 models:
class ProductLine < ActiveRecord::Base
has_many :specifications
has_many :specification_categories, :through => :specifications,
end
class Specification < ActiveRecord::Base
belongs_to :product_line
belongs_to :specification_category
end
class SpecificationCategory < ActiveRecord::Base
has_many :specifications
has_many :product_lines, :through => :specifications
end
Basically, we are showing the specifications as a subset of data on the product line page and we would like to do something like (example only, yes I'm aware of N+1):
Controller:
#product_line = ProductLine.find(params[:id])
#specification_categories = #product_line.specification_categories)
View:
#specification_categories.each do |specification_category|
...
specification_category.specifications.each do |specification|
...
end
end
The issue here is getting rails to filter the specifications by ProductLine. I've tried constructing queries to do this but it always generates a separate NEW query when the final association is called. Even though we aren't using the code above now (not a good idea here, since we could potentially run into an N+1 problem), I'd like to know if it's even possible to do a 3 way join with association filtering. Has anyone run across this scenario? Can you please provide an example of how I would accomplish this here?
Prevent the N+1 by altering your line to:
#specification_categories = #product_line.specification_categories).include(:specifications)
OR
Construct your own query using .joins(:association), and do the grouping yourself.