I have rails model as this:
Stock:
has_one :location
has_one :product
Product:
belongs_to :stock
Location:
belongs_to :stock
In DB Stock has two foreign key columns:
location_id
product_id
When querying stock, I want to order by name column of location and product (location and then product), how to join these three table and sort?
Currently my biggest challenge is how to join (even two tables), I constantly get wrong column name in SQL output, here is an example:
SELECT "stocks".* FROM "stocks" INNER JOIN "locations" ON "locations"."id" = "stocks"."id"
Where it should be:
SELECT "stocks".* FROM "stocks" INNER JOIN "locations" ON "locations"."id" = "stocks"."location_id"
I learned that I may be missing some foreign_key attribute in models, but don't know how to add them.
It seems like you placed the foreign keys in a wrong table http://guides.rubyonrails.org/association_basics.html#the-has-one-association
'products' and 'locations' tables both should have 'stock_id' for this code to work:
class Stock < ActiveRecord::Base
has_one :location
has_one :product
end
class Location < ActiveRecord::Base
belongs_to :stock
end
class Product < ActiveRecord::Base
belongs_to :stock
end
Related
I have 3 models as following.
class Order
belongs_to :item
belongs_to :category
end
class Item
has_many :orders
belongs_to :category
end
class Category
has_many :items
has_many :orders, through: :items
end
I want to join the tables like Order.joins(:item).joins(:category), but it's not working.
Desired SQL is
SELECT * FROM `orders`
INNER JOIN `items` ON `items`.`id` = `orders`.`item_id`
INNER JOIN `categories` ON `items`.`category_id` = `categories`.`id`
I hope your helps.
I'm a little confused because Order and Item both belongs_to Category and Category already has_many Orders with that setup, the :through option is unnecesary.
For your desired output I guess you want to do a nested join (order > item > category) instead of multiple joins (order > item+category)
https://guides.rubyonrails.org/active_record_querying.html#joining-multiple-associations
12.1.3.1 Joining Nested Associations (Single Level)
Article.joins(comments: :guest)
This produces:
SELECT articles.* FROM articles
INNER JOIN comments ON comments.article_id = articles.id
INNER JOIN guests ON guests.comment_id = comments.id
So, you should do something like Order.joins(item: :category)
The syntax you're looking for is
Order.joins(item: :category)
Check here for more information.
The proper way to setup these associations is:
class Order < ApplicationRecord
belongs_to :item
# This references items.category_id
has_one :category, through: :item
end
class Item < ApplicationRecord
has_many :orders
belongs_to :category
end
class Category < ApplicationRecord
has_many :item, through: :orders
has_many :orders
end
You want to remove the orders.category_id column (if it exists) and use an indirect association through the items table to avoid duplication. The semantics of belongs_to and has_one can be confusing but belongs_to assumes that the foreign key is on this model (orders), while has_one places it on the other models table (items).
This will let you join/include/eager_load the association with:
irb(main):002:0> Order.joins(:category)
Order Load (1.4ms) SELECT "orders".* FROM "orders" INNER JOIN "items" ON "items"."id" = "orders"."item_id" INNER JOIN "categories" ON "categories"."id" = "items"."category_id" LIMIT $1 [["LIMIT", 11]]
And as you can see Rails will handle joining the join table (items) automatically.
If you want both associations to be loaded you can use a hash or just list both:
Order.eager_load(item: :category)
Order.eager_load(:item, :category)
class Article < ActiveRecord::Base
has_many :books
end
class Book < ActiveRecord::Base
belongs_to :article
end
Is there a way to access all books of a set of authors ?
For example: Article.where(...).books that will return the associated books of selected articles?
You can do this using a subquery:
Book.where(article: Article.where(...))
Note that Activerecord still turns this into a single SQL query:
SELECT `books`.* FROM `books` WHERE `books`.`article_id` IN (SELECT `articles`.`id` FROM `articles` WHERE (...))
I am trying to get the employees of a business with the highest points in placements for each month.
The schema looks like this:
class Business < ActiveRecord::Base
has_many :employees
has_many :placements, through: :employees
class Employee < ActiveRecord::Base
attr_accessible :name
belongs_to :business
has_many :placements
class Placement < ActiveRecord::Base
attr_accessible :month, :employee_id, :points
belongs_to :employee
I have tried the following:
#business.placements.includes(:employee).group(:month).order('points DESC').map(&:employee)
But I get a PostgreSQL group_by error:
: SELECT "placements".* FROM "placements" INNER JOIN "employees" ON "placements"."employee_id" = "employees"."id" WHERE "employees"."business_id" = 43 GROUP BY month ORDER BY points DESC
ActiveRecord::StatementInvalid: PG::Error: ERROR: column "placements.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT "placements".* FROM "placements" INNER JOIN "employee...
Any help will be appreciated.
When you group any columns you select either need to be in the group_by or have some aggregation function on them. By default AR pulls back all the columns so you probably want to restrict that with a .select
I am using rails and graphing some data. I use the following:
<%= column_chart User.includes(:levels).group(:email).sum(:score) %>
How do i make this group command sort the returned array by score from highest to lowest?
My models are arranged as follows
class User < ActiveRecord::Base
has_many :games
contains id, email
end
class Game < ActiveRecord::Base
has_many :levels
belongs_to :user
#contains id, user_id, name
accepts_nested_attributes_for :levels
end
class Level < ActiveRecord::Base
belongs_to :game
#contains id, score and game_id
end
Is your score in Level or in User ?
OK, they're in a deeper nested relation.
You can make your life easier, if your User model declares that:
class User < ActiveRecord::Base
has_many :games
has_many :levels, through: :games
end
Then you have to join the levels.
Looking at the SQL generated by ActiveRecord, you can see that
User.joins(:levels).group(:email).sum(:score)
generates
SELECT sum(score) AS sum_score, email FROM users INNER JOIN games ON games.user_id = users.id INNER JOIN levels ON levels.games_id=games.id GROUP BY email
As sum doesn't return a Relation, but an ActiveSupport::OrderedHash, you cannot append .order() to it.
What you can do, is inject the order before the sum:
User.joins(:levels).group(:email).order('sum_score DESC').sum(:score)
generates
SELECT sum(score) AS sum_score, email FROM users
INNER JOIN games ON games.user_id = users.id
INNER JOIN levels ON levels.games_id=games.id
GROUP BY email
ORDER BY sum_score DESC
which is, what you are looking for.
I've got the an has_one association:
has_one association user -> customer models
will the user have the customer_id or customer will have the user_id attribute?
other question: into my _form i'd like to have a select/option with all the users that hasn't associated with a customer which is the best way to do that?
thanks a lot.
The _id field is always in the model with the belongs_to, and refers to the other table name.
class Customer < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_one :customer
end
In this case, the customers table will have a user_id field.
For the second question, missing values are found in SQL using outer joins.
The SQL you want would be
select
from users
left outer join customers on users.id = customers.user_id
where customers.id is null
In ActiveRecord, add a scope to your User class.
In Rails 3.x:
class User < ActiveRecord::Base
has_one :customer
scope :missing_customer,
includes(:customer).where("customers.id is null")
end
In Rails 2.3.x:
class User < ActiveRecord::Base
named_scope :missing_customer,
{ :joins => "left outer join customers on users.id = customers.user_id",
:conditions => "customers.id is null" }
end