Ruby on Rails - use where with array of object - ruby-on-rails

my Post object has post_categories array
Post.post_categories = []
For my all Post object i need to filter posts by post_categories which include 1
like this
Post.all.where :post_categories.include? 1
how can i do
these are models
class Post < ActiveRecord::Base
has_many :categorizes
has_many :post_categories, :through=>:categorizes
accepts_nested_attributes_for :post_categories
end
class PostCategory < ActiveRecord::Base
has_many :categorizes
has_many :posts, :through=>:categorizes
end
class Categorize < ActiveRecord::Base
belongs_to :post
belongs_to :post_category
end

You can use a join and filter on the ID field of the joined table:
Post.joins(:post_categories).where('post_categories.id' => 1)

Related

Setting up Rails model

I have a User model, Post model and Bookmark model. How do i need to set up relationship among them so that I can use current_user.bookmarks.posts.
Maybe:
class User < ApplicationRecord
has_many :bookmarks
end
class Bookmark < ApplicationRecord
belongs_to :user
has_many :posts
end
class Post < ApplicationRecord
belongs_to :bookmark
end
If you're wanting to get all the posts that belong to the user, then you can use the has_many :through association:
class User < ApplicationRecord
has_many :bookmarks
has_many :posts, through: :bookmarks
end
class Bookmark < ApplicationRecord
belongs_to :user
has_many :posts
end
class Post < ApplicationRecord
belongs_to :bookmark
end
Then you can just call:
user = User.first
all_posts = user.posts
It will return an array containing all the posts for each of the bookmarks belonging to the user.

Rails naming convention for join tables to specialized tables

I get that if you have a posts and a categories table that the join table will be posts_categories. However you might have more than one type of category.
If we decide to create specialized category tables for each object type we would create a posts_categories table which would be a table of categories specifically for post objects. What would the many-to-many join table be called between posts and posts_categories?
If I were you, I would create the join table (categorizations) with additional column(s):
rails g model Categorization post:references category:references new_column:new_type ....
### models/post.rb
class Post < ApplicationRecord
has_many :categorizations
has_many :categories, through: :categorizations
end
## models/categorization
class Categorization < ApplicationRecord
belongs_to :post
belongs_to :category
## You can add new columns as many as you want, just like other tables
end
# models/category.rb
class Category < ApplicationRecord
has_many :categorizations
has_many :posts, through: :categorizations
end
I'm not sure I can fully understand your question. But, if you want create a many-to-many relationship between Post and Category, you can try as bellow:
# post.rb
class Post < ApplicationRecord
has_many :post_categories
has_many :categories, through: :post_categories
end
and
#post_category.rb
class PostCategory < ApplicationRecord
belongs_to :post
belongs_to :category
end
and
# category.rb
class Category < ApplicationRecord
has_many :post_categories
has_many :posts, through: :post_categories
end
Hope it helps.

counter_cache for multi level association

I have 3 models: Topic, Post, Link.
class Topic < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
has_many :links
belongs_to :topic
end
class Link < ActiveRecord::Base
belongs_to :post
end
I'd like to have counter_cache for forums on Link model.
How can I do this ?
Check out the counter_culture gem.
From the readme:
class Product < ActiveRecord::Base
belongs_to :sub_category
counter_culture [:sub_category, :category]
end
class SubCategory < ActiveRecord::Base
has_many :products
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :sub_categories
end
In the above example, the Category model will keep an up-to-date counter-cache in the products_count column of the categories table.

complex query... how to join many classes in rails?

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')

Best way to do the following join in Rails 3

I have the following classes:
class Annotation < ActiveRecord::Base
has_many :annotation_tags
end
class Tag < ActiveRecord::Base
has_many :annotation_tags
end
class AnnotationTag < ActiveRecord::Base
belongs_to :annotation
belongs_to :tag
end
and the following join:
SELECT `annotations`.*, annotation_tags.* FROM `annotations` JOIN annotation_tags on
annotation_tags.annotation_id = annotations.id and annotation_tags.tag_id = 123
What is the best way to code this in Rails 3?
You have two options:
Use has_many, :xs, :through => :ys.
class Annotation < ActiveRecord::Base
has_many :annotation_tags
has_many :tags, :through => :annotation_tags
end
class Tag < ActiveRecord::Base
has_many :annotation_tags
has_many :annotations, :through => :annotation_tags
end
class AnnotationTag < ActiveRecord::Base
belongs_to :annotation
belongs_to :tag
end
Then you can type:
tag = Tag.find(123)
annotations = tag.annotations
Alternatively, if you don't need extra attributes on your AnnotationTag model, i.e. it's purely a join table, you can use has_and_belongs_to_many. Your join table must not have an id column, so in the migration, make sure you specify :id => false as described in the ActiveRecord Associations API docs
class Annotation < ActiveRecord::Base
has_and_belongs_to_many :tags
end
class Tag < ActiveRecord::Base
has_and_belongs_to_many :annotations
end
class AnnotationsTag < ActiveRecord::Base # First part of model name must be pluralized.
belongs_to :annotation
belongs_to :tag
end
In this case the syntax for getting all the annotations for a tag is the same.
tag = Tag.find(123)
annotations = tag.annotations
Here was my initial attempt:
tag_id = 123
Annotation.joins("JOIN #{AnnotationTag.table_name} on #{AnnotationTag.table_name}.annotation_id = #{Annotation.table_name}.id and #{AnnotationTag.table_name}.tag_id = #{tag_id}").scoped
#Cameron's is a much cleaner solution, but did require my join table class name to be changed to AnnotationsTags (note the plural).

Resources