I use counter_culture gem, but when I want to use it, it throws an error that it cannot find product_id field in Product model, which makes sense because it should look for 'id'.
Below is the query generated when I call 'Product.counter_culture_fix_counts'. As you can see, it produced 'LEFT JOIN products AS products ON product_categories.id = products.product_id', which is not correct and it should be 'LEFT JOIN products AS products ON product_categories.product_id = products.id'.
SELECT categories.id, categories.id, COUNT(products.id) AS count, categories.products_count FROM "categories" LEFT JOIN product_categories AS product_categories ON categories.id = product_categories.category_id LEFT JOIN products AS products ON product_categories.id = products.product_id AND (products.active = 't') GROUP BY "categories"."id" ORDER BY "categories"."id" ASC LIMIT 1000 OFFSET 0
And here are the models:
# Product Model
class Product
has_one :product_category, dependent: :destroy
has_one :category, :through => :product_category
counter_culture [:product_category, :category], :column_name => Proc.new { |product| 'products_count' }, :column_names => { ["products.active = ?", true] => 'products_count' }, :touch => true
end
# ProductCategory Model
class ProductCategory
belongs_to :product
belongs_to :category
end
# Category Model
class Category
has_many :product_categories, dependent: :destroy
has_many :products, :through => :product_categories
end
Any idea how to get this working?
Thank you, Miro
Related
I’m using Rails 4.2. I have the following user model with a couple of has_many associations
class User < ActiveRecord::Base
…
has_many :roles, through: :roles_users
has_many :addresses, dependent: :destroy, as: :addressable, inverse_of: :addressable
class Role < ActiveRecord::Base
has_and_belongs_to_many :users
has_many :roles_users
class RolesUser < ActiveRecord::Base
belongs_to :user
belongs_to :role
end
class Address < ActiveRecord::Base
belongs_to :addressable, polymorphic: true
alias :user :addressable
I would like to find all users of a specific role without any addresses. I thought the below would do it
> users = User.includes(:roles, :addresses).where(:roles => {:name => 'User'}, :addresses => {:user_id => nil})
But when I check the results, I’m still getting results that have addresses …
2.7.1 :012 > users.last.addresses.count
…
=> 2
What’s the proper way to write a finder that queries these two has_many associations?
Checking for children records with a nil parent id is like the way of doing this in Rails 4. But if that doesn't work, you could use the NOT IN clause combination:
User
.where
.not(
id: User
.joins(:addresses, :roles)
.where(roles: { name: 'User' })
.select(:id)
)
It's basically filtering out by the user id all those user rows that have an address associated, have a role, and the role name is exactly "User". You end up with a SQL query like this:
SELECT "users".*
FROM "users"
WHERE "users"."id" NOT IN (
SELECT "users"."id"
FROM "users"
INNER JOIN "roles_users" ON "roles_users"."user_id" = "users"."id"
INNER JOIN "roles" ON "roles"."id" = "roles_users"."role_id"
WHERE "roles"."name" = 'User'
)
I've a simply rails application. It contains two models. Employee and EmployeeManager. I Basically want an Employee to have one or more Managers (other Employees) and I wish to be able to query these as you would expect.
Here's my Employee:
class Employee < ActiveRecord::Base
attr_accessible :name
has_many :employee_managers
has_many :managers, through: :employee_managers
has_many :employees, through: :employee_managers
end
And the manager model:
class EmployeeManager < ActiveRecord::Base
attr_accessible :employee_id, :manager_id
belongs_to :employee, foreign_key: "employee_id"
belongs_to :manager, class_name: "Employee", foreign_key: "manager_id"
end
This looks OK to me, however when I go to query employees and managers off an Employee Rails is using the same column (employee_id=X):
irb(main):008:0> Employee.first
Employee Load (0.3ms) SELECT "employees".* FROM "employees" LIMIT 1
=> #
irb(main):009:0> Employee.first.managers
Employee Load (0.1ms) SELECT "employees".* FROM "employees" LIMIT 1 Employee Load (0.1ms) SELECT "employees".* FROM "employees" INNER JOIN "employee_managers" ON "employees"."id" = "employee_managers"."manager_id" WHERE "employee_managers"."employee_id" = 1
=> []
irb(main):010:0> Employee.first.employees
Employee Load (0.4ms) SELECT "employees".* FROM "employees" LIMIT 1 Employee Load (0.3ms) SELECT "employees".* FROM "employees" INNER JOIN "employee_managers" ON "employees"."id" = "employee_managers"."employee_id" WHERE "employee_managers"."employee_id" = 1
=> []
How do I fix this last query? It should be using manager_id not employee_id!
Thanks in advance :)
Set the foreign_key in the Employee model, and add and inverse relation (its the same Model)
class Employee < ActiveRecord::Base
has_many :employee_managers
has_many :managers, through: :employee_managers
has_many :manager_employees, :class_name => "EmployeeManager", :foreign_key => "manager_id"
has_many :employees, through: :manager_employees, :source => "Employee"
end
Other changes:
You don't need attr_accessible :name or attr_accessible :employee_id, :manager_id. Just add attr_accessible for fields that are not in the database.
I have 4 models: Products, Vendors, Categories, CategoryProducts (join model).
A vendor has many products.
A product belongs_to a vendor.
A product has many categories.
A vendor has many categories through products.
This is how my models are setup now:
Vendor.rb
class Vendor < ActiveRecord::Base
attr_accessible :name, :description, :category_ids, :product_ids, :user_id
has_many :products, :dependent => :destroy
has_many :categories, :through => :products
belongs_to :owner, :class_name => "User",
:foreign_key => "user_id"
end
Product.rb
class Product < ActiveRecord::Base
attr_accessible :name, :description, :price, :vendor_id, :category_ids
belongs_to :vendor
has_many :category_products do
def with_categories
includes(:category)
end
end
has_many :categories, :through => :category_products
end
Category.rb
class Category < ActiveRecord::Base
attr_accessible :name, :product_ids, :category_ids
has_many :category_products do
def with_products
includes(:product)
end
end
has_many :products, :through => :category_products
end
The join model for Product & Category:
CategoryProduct.rb
class CategoryProduct < ActiveRecord::Base
attr_accessible :product_id, :category_id, :purchases_count
belongs_to :product
belongs_to :category
validates_uniqueness_of :product_id, :scope => :category_id
end
When I try to get the categories of a vendor on my command-line, it returns a lot of duplicate results - largely because it is essentially returning the category for each product owned by that vendor.
Here is an example, where v = Vendor.first:
1.9.3p194 :008 > v.products.count
(0.3ms) SELECT COUNT(*) FROM "products" WHERE "products"."vendor_id" = 10
=> 8
1.9.3p194 :009 > v.categories.count
(0.3ms) SELECT COUNT(*) FROM "categories" INNER JOIN "category_products" ON "categories"."id" = "category_products"."category_id" INNER JOIN "products" ON "category_products"."product_id" = "products"."id" WHERE "products"."vendor_id" = 10
=> 13
1.9.3p194 :010 > Category.count
(7.8ms) SELECT COUNT(*) FROM "categories"
=> 2
Some products have multiple categories, that's why there is a discrepancy between v.products.count and v.categories.count.
How do I get v.categories.count to just show me the unique amount of categories (in this case, the max is 2)?
Thanks.
I think it is fairly simple. Just use the uniq method as follows.
v.categories.uniq.count
To put it at association level you can use :uniq => true option as follows.
has_many :categories, :uniq => true
I have a situation in which Schools and EventLeg records are tied together through Photos, which belong to a given Event.
class Photo < ActiveRecord::Base
belongs_to :event
belongs_to :event_leg
has_and_belongs_to_many :schools
end
class Event < ActiveRecord::Base
has_many :event_legs, :through => :photos, :group => 'event_legs.id'
has_many :photos
end
class School < ActiveRecord::Base
has_and_belongs_to_many :photos
has_many :events, :through => :photos
has_many :event_legs, :through => :photos
end
class EventLeg < ActiveRecord::Base
has_many :photos
has_many :schools, :through => :photos
end
I need to get the schools that appeared in a given event leg at an event.
#event = Event.find 4088
#event.schools.joins(:photos).where('photos.event_leg_id' => 28034)
This results in SQL that joins the photos_schools table twice, once to schools and once to photos:
SELECT DISTINCT `schools`.* FROM `schools`
INNER JOIN `photos_schools` `photos_schools_join` ON `photos_schools_join`.`school_id` = `schools`.`id`
INNER JOIN `photos` `photos_schools_2` ON `photos_schools_2`.`id` = `photos_schools_join`.`photo_id`
INNER JOIN `photos_schools` ON `schools`.`id` = `photos_schools`.`school_id`
INNER JOIN `photos` ON `photos_schools`.`photo_id` = `photos`.`id`
WHERE `photos`.`event_id` = 4088 AND `photos`.`event_leg_id` = 28034
The second JOIN of photos_schools -> photos is unnecessary, the following query does the same thing and faster:
SELECT DISTINCT `schools`.* FROM `schools`
INNER JOIN `photos_schools` ON `schools`.`id` = `photos_schools`.`school_id`
INNER JOIN `photos` ON `photos_schools`.`photo_id` = `photos`.`id`
WHERE `photos`.`event_id` = 4088 AND photos.event_leg_id = 28034;
So why is AR creating this second set of joins, and how can I make it stop?
try skipping the joins(:photos) i guess it should work
#event.schools.where('photos.event_leg_id' => 28034)
I have got this 'query'
#product_collections = ProductCollection.
#includes(:products). #does not do anything
joins(:products). #this at least gives me the products (in articles table)
group("tags.id").
where("articles.category_id = ?", #category.id).
where("articles.available = ?", true).
order('tags.name asc')
this produces the following sql
SELECT "tags".* FROM "tags"
INNER JOIN "article_taggings" ON "tags"."id" = "article_taggings"."tag_id"
INNER JOIN "articles" ON "articles"."id" = "article_taggings"."article_id"
WHERE ("tags"."type" = 'ProductCollection')
AND (articles.category_id = 1)
AND (articles.available = 't')
GROUP BY tags.id
ORDER BY tags.name asc
how could I manage to get the products in one (or only one second) go?
Models:
class Article < ActiveRecord::Base
has_many :article_taggings
has_many :tags, :through => :article_taggings
end
class Product < Article
belongs_to :category
has_many :product_collections, :through => :article_taggings, :source => :tag
end
class ArticleTagging < ActiveRecord::Base
belongs_to :article
belongs_to :tag
end
class Tag < ActiveRecord::Base
has_many :article_taggings
has_many :articles, :through => :article_taggings
has_and_belongs_to_many :web_pages
end
class ProductCollection < Tag
has_many :products, :through => :article_taggings, :source => :article
end
You need to put the includes after joins. That will solve the problem.