I have following three models
class Rating < ActiveRecord::Base
belongs_to :user
belongs_to :book
end
class User < ActiveRecord::Base
attr_accessible :name, :dob, :mobile
has_many :books, :through => 'ratings'
end
class Book < ActiveRecord::Base
attr_accessible :book_name, :author, :pages
has_many :users, :through => 'ratings'
end
Now I have to find all the "book_name"s of each book which is related the respective user and store it in the array.
Here is code
#book_names = []
#books = Rating.find(:all, 'user_id = ?', current_user.id)
#books.each do |book|
book_info = Book.find(book.id)
#book_names << book_info.book_name
end
Is there any other way for the same or join method.
Yes, there is simple way to do it. Try
#book_names= current_user.books.map(&:book_name)
Related
My data model is fellows, the note and hashtag's relationship is many to many
class Note < ActiveRecord::Base
attr_accessible :title
attr_accessible :content
attr_accessible :created_at
default_scope -> { order(created_at: :desc) }
has_and_belongs_to_many :hashtags
end
class Hashtag < ActiveRecord::Base
attr_accessible :name
has_and_belongs_to_many :notes
end
class NoteHashtag < ActiveRecord::Base
t.belongs_to :note, index: true
t.belongs_to :hashtag, index: true
end
I want to inquire the sql like:
select Note.* from Note inner join NoteHashtag on Note.id=NoteHashtag.note inner join Hashtag on NoteHastag.hashtag=Hashtag.id where Hashtag.name="test"
How to convert the sql into the datamodel operation in ruby on rails4?
I try to use the:
#note=Note.joins(:hashtag).where(name: "test")
and the error is:
ActionView::Template::Error (Association named 'hashtag' was not found on Note;perhaps you misspelled it?):
You need has_many :through associations if you are going to explicitly define the join model NoteHashtag. If you delete that model, you can do #notes=Note.joins(:hashtag).where(name: "test") and ActiveRecord will generate the query you are expecting.
Otherwise you could do
class Note < ActiveRecord::Base
...
has_many :note_hashtags
has_many :hashtags, through: :note_hash_tags
end
class Hashtag < ActiveRecord::Base
attr_accessible :name
end
class NoteHashtag < ActiveRecord::Base
belongs_to :note
end
Then #notes = Note.joins(:note_hashtags).joins(:hash_tags).where(name: "test) would work.
Note that both of these will return more than one note.
You can get records from many to many relationship by doing this:-
#note = Note.joins(:hashtags).where('hashtags.name' => 'test')
Why don't you address this relationship originating from the proper model? Define your models like this:
class Note < ActiveRecord::Base
...
has_many :note_hashtags
has_many :hashtags, through: :note_hashtags
end
class Hashtag < ActiveRecord::Base
attr_accessible :name
has_many :note_hashtags
has_many :notes through: :notes_hashtags
end
class NoteHashtag < ActiveRecord::Base
belongs_to :note
belongs_to :hashtag
end
And then query like this:
Hashtag.where(name: 'Test').notes
Hello i have this models
models/store/store.rb
class Store::Store < ActiveRecord::Base
attr_accessible :name
has_many :store_products
has_many :products, :through => :store_products
end
models/product.rb
class Product < ActiveRecord::Base
attr_accessible :name ...
has_many :store_products
has_many :stores, :through => :store_products
end
models/store/store_product.rb
class Store::StoreProduct < ActiveRecord::Base
set_table_name "stores_products"
attr_accessible :store_id, :product_id
belongs_to :store
belongs_to :product
end
and im getting params['store_ids'] via post to controller action
>> params['store_ids']
=> ["1", "2"]
where i have this code
>> #products = Product.joins(:stores).where("stores.id IN (?)", params[:store_ids])
it throw error
#<NameError: uninitialized constant Product::StoreProduct>
How can i fix this (select products only in some stores) ? :-) Thank you
edit : MORE INFO :
Folder Structure
app/controllers/store/main_controller.rb
app/controllers/store/stores_controller.rb
app/models/store/store.rb
app/models/store/store_product.rb
app/models/product.rb
the code is in
class Store::MainController < ApplicationController
def index
if params['store_ids'] then
#products = Product.joins(:stores)...
else
#products = Product.paginate page: params[:page], order: 'name asc', per_page: 10
end
end
Part of DB Schema:
stores_products
id
product_id
store_id
products
id
name
...
stores
id
name
...
SOLUTION (thanks to Gotva)
class Product < ActiveRecord::Base
attr_accessible :name, ...
has_many :store_products, class_name: "Store::StoreProduct"
has_many :stores, :through => :store_products
end
class Store::Store < ActiveRecord::Base
attr_accessible :name
has_many :store_products, class_name: "Store::StoreProduct"
has_many :products, :through => :store_products
end
class Store::StoreProduct < ActiveRecord::Base
set_table_name "stores_products"
attr_accessible :store_id, :product_id
belongs_to :store, class_name: "Store::Store"
belongs_to :product, class_name: "Product"
end
and finally
#products = Product.joins(:stores).where("stores.id IN (?)", params[:store_ids]).paginate(page: params[:page], order: 'products.name asc', per_page: 10)
try this
#products = Product.joins(:stores).where("#{Store::Store.table_name}.id IN (?)", params[:store_ids]).paginate(page: params[:page], order: 'name asc', per_page: 10)
maybe it will duplicate products so add method uniq after where, this applies distinct in the query
I have Product and Categories models.
Each Product can have many Categories. Each category will have many products.
Am I better off doing 2 HABTM or has_many relations?
I have been told that HABTM is being deprecated - is that true?
Also, for both, I assume I will have to use a join table categories_products. How do I create that? Just a regular migration?
Thanks.
Example of top(number). Updated.
class Product < ActiveRecord::Base
has_many :category_products do
def with_categories
includes(:category)
end
end
has_many :categories, :through => :category_products
def top_categories(number)
category_products.with_categories.order("purchases_count DESC").limit(number).map {|c| c.category} # category included
end
end
class Category < ActiveRecord::Base
has_many :category_products do
def with_products
includes(:product)
end
end
has_many :products, :through => :category_products
def top_products(number)
category_products.with_products.order("purchases_count DESC").limit(number).map {|c| c.product} # product included
end
end
class CategoryProduct < ActiveRecord::Base
belongs_to :product
belongs_to :category
validates_uniqueness_of :product_id, :scope => :category_id
# attribute :purchases_count, :default => 0
end
I have a Post model that belongs to a single category and author. Users may create "favorites" for categories and authors. How can I most efficiently query a list of all posts, but with the visitor's preferred categories and/or authors sorted to the top?
class Post < ActiveRecord::Base
belongs_to :category
belongs_to :author
end
class Favorite < ActiveRecord::Base
belongs_to :user
belongs_to :category # favorite category
belongs_to :author # favorite author
end
class User < ActiveRecord::Base
has_many :favorites
end
class User < ActiveRecord::Base
has_many :favorites
has_many :favorite_categories, :through => :favorites, :source => :category
has_many :favorite_authors, :through => :favorites, :source => :author
end
class Favorite < ActiveRecord::Base
belongs_to :user
belongs_to :category # favorite category
belongs_to :author # favorite author
end
class Post < ActiveRecord::Base
belongs_to :category
belongs_to :author
named_scope :order_by_user, lambda {|user| where(
:category_id => user.favorite_categories.map(&:id),
:author_id => user.favorite_authors.map(&:id)
)}
end
user = User.first
posts = Post.order_by_user(user)
Alternate: less number of queries, but user model fetches data from Favorite
class Favorite < ActiveRecord::Base
belongs_to :user
belongs_to :category # favorite category
belongs_to :author # favorite author
end
class User < ActiveRecord::Base
has_many :favorites
def favorite_category_ids
Favorite.where(:user_id => self.id).select(:category_id).map(&:category_id).compact
end
def favorite_author_ids
Favorite.where(:user_id => self.id).select(:author_id).map(&:author_id).compact
end
end
class Post < ActiveRecord::Base
belongs_to :category
belongs_to :author
named_scope :order_by_user, lambda {|user| where(
:category_id => user.favorite_category_ids,
:author_id => user.favorite_author_ids
)}
end
user = User.first
posts = Post.order_by_user(user)
This code is not tested, but gives the idea.
Say I have these models
class Project < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :project
belongs_to :user
end
class User < ActiveRecord::Base
has_many :comments
end
So that I can do
p = Project.find(1, :include => :comments)
p.comments.collect(&:user).collect(&:name) # this executes select for each user
How do I say I want to also include comment's user?
I believe :include => {:comments => :user} should work.