rails 4.2
ruby 2.1
I have two very basic models (product and tag) with has_many association through another model (taggings).
I have another model (category) with one-to-many connection with the aforementioned model (product).
Question:
How to show in view the tag list of products with a specific product's category?
In other words: Is it possible to list all tags from a particular category of product?
Models:
class Product < ActiveRecord::Base
has_many :taggings
has_many :tags, through: :taggings
belongs_to :category, counter_cache: true
end
class Tag < ActiveRecord::Base
has_many :taggings
has_many :products, through: :taggings
end
class Tagging < ActiveRecord::Base
belongs_to :product
belongs_to :tag, counter_cache: :products_count
end
class Category < ActiveRecord::Base
has_many :products
end
Quickest way is category_object.products.map(&:tags).flatten . Can be improved. :)
category has many products and product has many tags. Mapping tags method on each product. Flatten to remove duplicates.
You can add a product_tags association to the Category class:
class Category < ActiveRecord::Base
has_many :products
has_many :product_tags, -> { uniq }, through: :products
end
When you access the product_tags association, Rails will use a SELECT DISTINCT query so you won't end up with duplicate tags and the DB will eliminate duplicates.
If the above doesn't feel natural for your model, then you can also use the following (assuming c is a Category instance):
Tag.joins(:products).where(products: { category: c})
The DB query will be very similar to the other example.
Related
I have users table, books table and books_users join table. In the users_controller.rb I am trying extract the users who have filtered_books. Please help me to resolve that problem.
user.rb
has_many :books_users, dependent: :destroy
has_and_belongs_to_many :books, join_table: :books_users
book.rb
has_and_belongs_to_many :users
books_user.rb
belongs_to :user
belongs_to :book
users_controller.rb
def filter_users
#filtered_books = Fiction.find(params[:ID]).books
#users = **I want only those users who have filtered_books**
end
has_and_belongs_to_many does not actually use a join model. What you are looking for is has_many through:
class User < ApplicationRecord
has_many :book_users
has_many :books, through: :book_users
end
class Book < ApplicationRecord
has_many :book_users
has_many :users, through: :book_users
end
class BookUser < ApplicationRecord
belongs_to :book
belongs_to :user
end
If you want to add categories to books you would do it by adding a Category model and another join table. Not by creating a Fiction model which will just create a crazy amount of code duplication if you want multiple categories.
class Book < ApplicationRecord
has_many :book_users
has_many :users, through: :book_users
has_many :book_categories
has_many :categories, through: :book_categories
end
class BookCategory < ApplicationRecord
belongs_to :book
belongs_to :category
end
class Category < ApplicationRecord
has_many :book_categories
has_many :books, through: :book_categories
end
If you want to query for users that follow a certain book you can do it by using an inner join with a condition on books:
User.joins(:books)
.where(books: { title: 'Lord Of The Rings' })
If you want to get books that have a certain category:
Book.joins(:categories)
.where(categories: { name: 'Fiction' })
Then for the grand finale - to query users with a relation to at least one book that's categorized with "Fiction" you would do:
User.joins(books: :categories)
.where(categories: { name: 'Fiction' })
# or if you have an id
User.joins(books: :categories)
.where(categories: { id: params[:category_id] })
You can also add an indirect association that lets you go straight from categories to users:
class Category < ApplicationRecord
# ...
has_many :users, though: :books
end
category = Category.includes(:users)
.find(params[:id])
users = category.users
See:
The has_many :through Association
Joining nested assocations.
Specifying Conditions on Joined Tables
From looking at the code i am assuming that Book model has fiction_id as well because of the has_many association shown in this line Fiction.find(params[:ID]).books. There could be two approaches achieve this. First one could be that you use #filtered_books variable and extract users from it like #filtered_books.collect {|b| b.users}.flatten to extract all the users. Second approach could be through associations using fiction_id which could be something like User.joins(:books).where(books: {id: #filtererd_books.pluck(:id)})
I have two models product and product_category
A product contains or mapped with multiple product categories and vice versa.
It is maintained through many to many relationships for which there is another model called products_in_category.
I am using Active Admin for backend and CRUD purpose and now I need to show multiple product_categories in Product index page of Active Admin.
Any suggestion or help will be great for me.
#app/models/product.rb
class Product < ApplicationRecord
has_many :products_in_categories
has_many :product_categories, through: :products_in_categories, dependent: :destroy
accepts_nested_attributes_for :product_categories
end
#app/models/product_category.rb
class ProductCategory < ApplicationRecord
has_many :products_in_categories
has_many :products, through: :products_in_categories, dependent: :destroy
accepts_nested_attributes_for :products
accepts_nested_attributes_for :products_in_categories
end
#app/models/products_in_category.rb
class ProductsInCategory < ApplicationRecord
belongs_to :product_category
belongs_to :product
end
You can list categories names (just change name with your actual attribute) in a column:
index do
# other columns goes here
column('Categories') { |p| p. product_categories.pluck(:name).join(', ') }
end
I want to search orders by tags, but tags are associated with customers.... is it possible to do that with Ransack?
Order.rb
Class Order < ActiveRecord::Base
belongs_to :customer
....
Customer.rb
Class Customer < ActiveRecord::Base
has_many :orders
has_many :customers_tags
has_many :tags, through: :customers_tags
....
Tag.rb
Class Tag < ActiveRecord::Base
has_many :customers_tags
has_many :customers, through: :customers_tags
....
Yes, you can search through multiple associations. Just reference them as you would in the association name (plural for has_many, singular for belongs_to).
So in your example, searching for a field fieldname, you would use:
customer_tags_fieldname_cont
(Replace fieldname with whatever field you want to search.)
I have next models: Articles, Announcements, Catalogs and Media.
For item of every model I need to create a subcategory and a category. I will plan to create a relationship table with two columns: parend_id and child_id, and a column for every model with category_id.
How many relationship models I should create?
One for all?
Or one relationship model for every model?
No need to create a relationship model instead just use belongs_to relationship
because you said following i guess belongs_to relations should do it
For item of every model I need create subcategory and category.
So just add subcategory_id and category_id in your tables: Articles, Announcements, Catalogs and Media and establish has_many belongs_to relationship
EDIT But if you insist for relationship model then I would suggest to use one relationship model for every model.
Last EDIT: I really think you should use belongs_to, has_many relation
Class Article < ActiveRecord::Base
belongs_to :category
belongs_to :subcategory
end
class Category < ActiveRecord::Base
has_many :articles
# the same relation(has_many) will be for Announcements, Catalogs and Media
has_many :subcategories
end
class Subcategory < ActiveRecord::Base
has_many :articles
belongs_to :category
end
In this way you can get all articles using category.subcategories.articles
If the category has only one Subcategory then change the relation between them to has_one and your syntax will become category.subcategory.articles
I would personally use a has_many :through relationship with a polymorphic association in a single join table:
#app/models/article.rb
Class Article < ActiveRecord::Base
has_many :categorizable_categories
has_many :categories, through: :categorizable_categories
end
class Article < ActiveRecord::Base
has_many :tags, as: :taggable,dependent: :destroy
has_many :categories, through: :tags
end
class Category < ActiveRecord::Base
has_many :tags, dependent: :destroy
has_many :articles, through: :tags, source: :taggable, source_type: 'Article'
end
class Tag < ActiveRecord::Base
belongs_to :taggable, polymorphic: true
belongs_to :category
end
This will allow you to call the categories for each model with the likes of #article.categories
To achieve the parent & child categories, I'd recommend using something like the Ancestry gem - you'd set up an ancestry column in your join table - basically allowing you to create relations between a model's categories directly
I'm having a hard time setting up the basic structure of my databases.
I have products (about 50). Each products is related to one or more place(s).
The basic schema would be this (no relation yet)
Products
id:integer
name:string
Places
id:integer
name:string
content:string
I first tought I could connect places and products by adding place_id to products and has_many belong_to in the controllers, but since a product can have more than one place, I don't know how.
If a Product has_many Places and a Place has_many Products your association needs to be many-to-many.
There are two ways to do this in Rails, the most recommended is a join model. You explicitly label the relationship between Products and Places. You could call it ProductLocation or Shop or similar, it would look like this:
product_locations:
product_id
place_id
class ProductLocation < ActiveRecord::Base
belongs_to :product
belongs_to :place
end
class Place < ActiveRecord::Base
has_many :product_locations
has_many :products, :through => :product_locations
end
class Product < ActiveRecord::Base
has_many :product_locations
has_many :places, :through => :product_locations
end
See http://guides.rubyonrails.org/association_basics.html#choosing-between-has_many-through-and-has_and_belongs_to_many
Just use through connection
ProductsPlaces (new table)
product_id
place_id
And in models
class Product < ActiveRecord::Base
has_many :places, :through => :products_places
end
class Place < ActiveRecord::Base
has_many :products, :through => :products_places
end
Also there is has_and_belongs_to_many which do in fact the same (with ProductsPlaces table)
class Product < ActiveRecord::Base
has_and_belongs_to_many :places
end
class Place < ActiveRecord::Base
has_and_belongs_to_many :products
end
But better use through, because has_and_belongs_to_many will be deprecated.
It sounds like you want to add product_id to places.
Product has_many Places
Place belongs_to Product