I have a Hotel model and HotelAmenity model and Amenity model which are related by has_many :through like this:
class Hotel < ActiveRecord::Base
has_many :hotel_amenities, :dependent => :destroy
has_many :amenities, through: :hotel_amenities
end
class Amenity < ActiveRecord::Base
has_many :hotel_amenities, :dependent => :destroy
has_many :hotels, through: :hotel_amenities
end
class HotelAmenity < ActiveRecord::Base
belongs_to :amenity
belongs_to :hotel
end
Now I have hotel_id in my action and I want to fetch all the hotels who have amenities [1,2,3] this is the array of amenity_id.
Good Question !!
Don't worry you can solve out this problem using join on the Hotel Model as :
First of all you will find the hotel by hotel_id
#hotel= Hotel.find_by_id(params[:user_id))
Now you can find the hotels with amenity_array [1,2,3]
#hotels = #hotels.joins(:hotel_amenities).where(hotel_amenities: { amenity_id: [1,2,3] }) // this can returns a hotel two or more time
#hotels = #hotels.joins(:hotel_amenities).where(hotel_amenities: { amenity_id: [1,2,3] }).uniq // this will return all hotels without redundant records
Hope this will work for you.
Try this code:
arr= [1,2,3]
Hotel.joins(:hotel_amenities).select("hotel_amenities.*,hotels.*").where("hotel_amenities.amenity_id= ?",arr)
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'm working on a simple Rails bookmarking app that lets users save Items and organize them in Collections.
I'd like to add an index page with staff picks that includes both selected Items and Collections. The result would be a feed that ranks these Items and Collections by the time they were selected as staff picks (not by the time they were initially created).
Here's what I have so far:
Feed Model
class Feed < ApplicationRecord
has_many :feed_entries, dependent: :destroy
has_many :items, through: :feed_entries
has_many :collections, through: :feed_entries
end
Feed Entry Model
class FeedEntry < ApplicationRecord
belongs_to :item
belongs_to :collection
belongs_to :feed
end
Item Model
class Item < ApplicationRecord
has_many :feed_entries, dependent: :destroy
has_many :feeds, through: :feed_entries
accepts_nested_attributes_for :feeds
end
Collection Model
class Collection < ApplicationRecord
has_many :feed_entries, dependent: :destroy
has_many :feeds, through: :feed_entries
accepts_nested_attributes_for :feeds
end
Feeds Controller
class FeedsController < ApplicationController
def index
#feed = Feed.find_by(feed_name: 'staffpicks')
#feed_items = #feed.items.includes(:feed_entries).order('feed_entries.created_at DESC')
#feed_collections = #feed.collections.includes(:feed_entries).order('feed_entries.created_at DESC')
end
end
So at this point I'm able to list staff-picked Items and Collections sorted by the time they were selected. But I can't figure out how to combine the two.
#everything = (#feed_collections + #feed_items)
I tried this, but I can't use order on an array and when I append .sort_by(&:created_at) it sorts by the creation date of the respective Item and Collection - not by when they were added to the array.
Any ideas how I can solve this?
Many thanks in advance. I'm new to programming and Rails, so your feedback is very much appreciated :)
class Feed < ApplicationRecord
has_many :feed_entries, dependent: :destroy,
-> { order(created_at: :desc) }
has_many :items, through: :feed_entries
has_many :collections, through: :feed_entries
end
class FeedEntry < ApplicationRecord
belongs_to :item, optional: true
belongs_to :collection, optional: true
belongs_to :feed
def entry
item || collection
end
end
#feed = Feed.eager_load(feed_entries: [:item, :collection]).find(1)
#entrees = #feed.feed_entries.map(&:entry)
You should also consider setting this up as a polymorphic association instead of using two different foreign keys as that will let you treat both items and collections as single homogenous association.
Just use the feed_entries association.
#feeds = feed_entries.includes(:item, :collection).order(“feed_entries.created_at desc”)
I am looking for a way to query a model based on children in a has_many through association.
I have 3 models:
class Conversation < ActiveRecord::Base
has_many :conversations_participants
has_many :participants, through: :conversations_participants
end
class ConversationsParticipant < ActiveRecord::Base
belongs_to :conversation
belongs_to :participant, class_name: 'User'
end
class User < ActiveRecord::Base
has_many :conversations_participants
has_many :conversations, through: :conversations_participants
end
I need to find conversations where participants matches an array of ids.
This is what i have at the moment (not working):
Conversation.includes(:participants).where(participants: params[:participants])
Sounds like you just want the conversations, if so you can joins.
Conversation.joins(:participants).where(:users => { :id => params[:participants] } )
Otherwise, if you want to eager load the participants, use includes
Conversation.includes(:participants).where(:users => { :id => params[:participants] } )
You can pass an array like this:
Conversation.includes(:participants).where(:id => params[:participants])
Assuming that params[:participants] is an array.
Conversation.includes(:participants).where( 'conversation_participants.participant_id' => params[:participants])
assuming that participant_id is the foreign key of participants in the conversation_participants table
I'm struggling to find all articles by knowing only categories users have subscribed to. Each articles can have many categories they belong to, my models look like
Article:
class Article < ActiveRecord::Base
has_many :article_categories
has_many :categories, through: :article_categories
Category:
class Category < ActiveRecord::Base
has_many :article_categories
has_many :articles, through: :article_categories
ArticleCategory:
class ArticleCategory < ActiveRecord::Base
belongs_to :article
belongs_to :category
article_categories table is just a storage for Article Categories with two columns: article_id && category_id
So how do I make proper query, hopefully with AR, if I have array of categories id's:
#ids = #categories.map { |c| c.id }
If I understand your question correction, this should do:
#articles = Article.joins(:article_categories).where(article_categories: { category_id: #ids })
You may have another table to keep track of user_categories (categories subscribed to by a user - user_id, category_id)
class User < ActiveRecord::Base
has_many :category_users
has_many :categories, :through => :category_users
def articles
Article.joins(:article_categories).where( { :category_id => category_users.pluck(:category_id) } ).distinct
end
end
I have the following associations:
class Venue < ActiveRecord::Base
has_many :sales
end
class Sale < ActiveRecord::Base
has_many :sale_lines
has_many :beverages, through: :sale_lines
end
class SaleLine < ActiveRecord::Base
belongs_to :sale
belongs_to :beverage
end
class Beverage < ActiveRecord::Base
has_many :sale_lines
has_many :sales, through: :sale_lines
has_many :recipes
has_many :products, through: :recipes
end
class Recipe < ActiveRecord::Base
belongs_to :beverage
belongs_to :product
end
class Product < ActiveRecord::Base
has_many :recipes
has_many :beverages, through: :recipes
end
I wan't to see the quantity of products sold by each venue, so basically I have to multiply the recipe.quantity by the sale_line.quantity of an specific product.
I would like to call #venue.calc_sales(product) to get the quantity sold of product.
Inside the class Venue I am trying to calculating it by:
class Venue < ActiveRecord::Base
has_many :sales
def calc_sales(product)
sales.joins(:sale_lines, :beverages, :recipes).where('recipes.product_id = ?', product.id).sum('sale_lines.quantity * recipe.quantity')
end
end
However, I can't access the recipes in that way.
Any idea on how to achieve it?
For the joins, you have to use a Hash to join a already-joined table. It's hard to explain, but here are some examples:
Venue.joins(:sales, :beverages) : This implies that the relations :sales and :beverages are declared on the Venue model.
Venue.joins(:sales => :beverages) : This implies that the relation :sales exists on the Venue model, and the relation :beverages exists on the Sale model.
Consider this:
Venue
has_one :sale
Venue.joins(:sales) : This would not work, you have to use the exact same name as the relation between the Venue model & Sale model.
Venue.joins(:sale) : This would work because you used the same name of the relation.
Attention: You have to use the pluralized name in the where clause:
Venue.joins(:sale).where(:sales => { :id => sale.id })
^^ ^^ # See the plural
In your case, you can do something like this:
sales.joins(:sale_lines => { :beverage => :recipes })
.where(:recipes => { :product_id => product.id })
.sum('sale_lines.quantity * recipes.quantity')