Many to many realationship and join in rails 3 - ruby-on-rails

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

How to use join to select many to many relationship model in ruby on rails4?

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

rails pagination and where clause with array

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

Use 2 has_many relations or 2 HABTM?

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

Rails: Prioritize/sort collection of records

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.

Recursive :include in Rails ActiveRecord

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.

Resources