About rails polymorphic - ruby-on-rails

I have three models , Article, User, Approval. and this is the Approval table
def change
create_table :approvals do |t|
t.references :user, foreign_key: true
t.references :approvable, polymorphic: true
t.timestamps
end
end
this is the three models
class User < ApplicationRecord
has_many :approvals
has_many :articles, dependent: :destroy
end
class Article < ApplicationRecord
belongs_to :user
has_many :approvals, as: :approvable
end
class Approval < ApplicationRecord
belongs_to :user
belongs_to :approvable, polymorphic: true
end
now i want to show an approval image in the article show page, and it's diffenrence according if the current_user has approved the article, i think there is a very eazy way to do this, but i can only come up with a stupid way to
do this, like :
<% #article.approvals.each do |approval| %>
<% if approval.user_id == current_user.id %>
# show has approvaled image
<% end %>
<% end %>
i think it maybe inefficient, please tell me a better solution, Thanks! : )
maybe i didn't express very well, i mean the approval is praise or like?

You can first check for approvals which belongs to current_user and article.
So in controller method use(considering you have only one article at a time),
#current_user_approvals = Approval.where(user_id: current_user.id, article_id: #article.id)
and then use #current_user_approvals in view as,
<% #current_user_approvals.each do |approval| %>
# show has approval image
<% end %>

Polymorphic should not be used in this case. you can use has_many :through to achieve approval. read the docs here
the migration would be:
class CreateApprovals < ActiveRecord::Migration[5.0]
def change
create_table :approvals do |t|
t.belongs_to :user
t.belongs_to :article
end
end
end
models would be like:
class User < ApplicationRecord
has_many :articles, dependent: :destroy
has_many :approvals
has_many :approved_articles, through: :approvals, source: :article
end
class Article < ApplicationRecord
belongs_to :user
has_many :approvals
has_many :approved_users, through: :approvals, source: :user
end
class Approval < ApplicationRecord
belongs_to :user
belongs_to :article
end
Now we can do the tricks (in rails console):
# create a user
user = User.create(name: 'user')
# create an article
article = user.articles.create(name: 'article')
# check whether article is approved
user.approved_articles.include?(article) # => false
# approve the article
user.approved_articles << article
# check check whether article is approved again
user.approved_articles.include?(article) # => true
edited version, since we do want to use polymorphic on approvals. the key is to use source_type to specify the polymorphic type (in this case it's 'Article')
class User < ApplicationRecord
has_many :articles, dependent: :destroy
has_many :approvals
has_many :approved_articles, through: :approvals, source: :approvable, source_type: 'Article'
end
class Article < ApplicationRecord
belongs_to :user
has_many :approvals, as: :approvable
has_many :approved_users, through: :approvals, source: :user
end
class Approval < ApplicationRecord
belongs_to :user
belongs_to :approvable, polymorphic: true
end
tests above in console still work.

Related

Implement a "Likes system" in rails

I'm trying to figure out how to setup the following. A user can create a review and then like his review or like other reviews if he wants to. I came up with the following setup:
class Like < ApplicationRecord
belongs_to :user
belongs_to :review
end
class User < ApplicationRecord
has_many :reviews
has_many :likes
has_many :liked_reviews, through: :likes, source: :review
end
class Review < ApplicationRecord
belongs_to :user
has_many :likes
has_many :liking_users, :through => :likes, :source => :user
end
class CreateLikes < ActiveRecord::Migration[6.0]
def change
create_table :likes do |t|
t.references :user, index: true
t.references :likes, index: true
t.integer :review_id
t.timestamps
end
end
end
I'm pretty sure that the model associations I came up with are correct, but I'm not so sure about the like table. Can someone please review the above and tell me if the model associations and like table are correct? Thanks in advance!

Rails polymorphic comments associate

Trying to work out an polymorphic association where Comments can belong to, for example, Photos and Users. Where a Comment on a user is treated as a "direct message". But I'm getting the User association a bit messed up.
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
class Photo < ActiveRecord::Base
has_many :comments, as: :commentable, dependent: :destroy
end
class User < ActiveRecord::Base
has_many :comments, dependent: :destroy
has_many :messages, as: :commentable
end
This is incorrect. Ideally, user.comments should retrieve all Comment records where user_id == user.id and something like user.messages should retrieve all Comments where the type is User and they are the subject.
Relationships:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
belongs_to :user
end
class Photo < ActiveRecord::Base
has_many :comments, as: :commentable, dependent: :destroy
end
class User < ActiveRecord::Base
has_many :comments, dependent: :destroy
has_many :messages, as: :commentable, class_name: 'Comment'
end
Schema:
# Comments
...
t.integer :user_id
t.integer :commentable_id
t.string :commentable_type
...
Then you can invoke:
#user.comments # Get all comments created by particular user
#user.messages # Get all comments where particular user is a subject
have you added the foreign key and type columns to the comments table? within a migration file:
def change
add_column :comments, :commentable_type, :integer
add_column, :commentable_type, :string
add_index :comments, [:commentable_type, :commentable_id]
end
Also make sure you have a Message model and it has the associations
class Message < ActiveRecord::Base
has_many :comments, dependent: :destroy
belongs_to :commentable, polymorphic: true
end

Ruby on Rails Nested Form

I have three Models: Deal, Zipcode, DealIncludeZipcode.
Now, the association looks like below:-
Deal Model:
class Deal < ActiveRecord::Base
has_many :deal_include_zipcodes, dependent: :destroy
has_and_belongs_to_many :zipcodes, dependent: :destroy
accepts_nested_attributes_for :deal_include_zipcodes,:reject_if => :reject_include_zipcodes, allow_destroy: true
private
def reject_include_zipcodes(attributes)
if attributes[:deal_id].blank? || attributes[:zipcode_id].blank?
if attributes[:id].present?
attributes.merge!({:_destroy => 1}) && false
else
true
end
end
end
end
class Zipcode < ActiveRecord::Base
has_and_belongs_to_many :deals
end
class DealIncludeZipcode < ActiveRecord::Base
belongs_to :deal
belongs_to :zipcode
end
Now in view I have a checkbox,on unchecking it I can select multiple zipcode to select from DealIncludeZipcode.But when I save the data it is not saving.
I have used migration for joining Zipcode and Deal Model in which my exclude zipcode functionality is working correctly.
Please provide a soloution.I have tried various method but didn't got succeed.
The whole point of has_and_belongs_to_many is that you don't have a model which joins the two parts.
class Deal < ActiveRecord::Base
has_and_belongs_to_many :zipcodes
end
class Zipcode < ActiveRecord::Base
has_and_belongs_to_many :deals
end
Would join through a "headless" table called deals_zipcodes. If you want to have a join model you need to use has_many :through instead.
class Deal < ActiveRecord::Base
has_many :deal_zipcodes, dependent: :destroy
has_many :zipcodes, through: :deal_zipcodes
end
class DealZipcode < ActiveRecord::Base
belongs_to :deal
belongs_to :zipcode
end
class Zipcode < ActiveRecord::Base
has_many :deal_zipcodes, dependent: :destroy
has_many :deals, through: :deal_zipcodes
end
I think Max's right. So your migration should be
create_table :deals do |t|
t.string :name
...
end
create_table :zipcodes do |t|
t.string :zipcode
...
end
create_table :deals_zipcodes do |t|
t.belongs_to :deal, index: true
t.belongs_to :zipcode, index: true
end
And your models should be
class Deal < ActiveRecord::Base
has_and_belongs_to_many :zipcodes
end
class Zipcode < ActiveRecord::Base
has_and_belongs_to_many :deals
end
You should probably take a look at the ActiveRecord guide, where you'll find more explanation.

Rails 4: can a child model belong_to two different parent models

In my initial Rails 4 app, I had the following models:
User
has_many :administrations
has_many :calendars, through: :administrations
has_many :comments
Calendar
has_many :administrations
has_many :users, through: :administrations
has_many :posts
has_many :comments, through: :posts
Administration
belongs_to :user
belongs_to :calendar
Post
belongs_to :calendar
has_many :comments
Comment
belongs_to :post
belongs_to :user
I just added a new Ad model to the app:
Ad
belongs_to :calendar
And now I would like to allow users to write comments about the ad records.
Can I use my existing Comment model and do something like:
Ad
belongs_to :calendar
has_many :comments
Comment
belongs_to :post
belongs_to :user
Or do I need to create a distinct "Comment" model, that I would call for instance AdComments or Feedback?
You need to use polymorphic associations. Something on the lines of this:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
class Ad < ActiveRecord::Base
has_many :comments, as: :commentable
end
class Product < ActiveRecord::Base
has_many :comments, as: :commentable
end
And the migration would look like:
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.references :commentable, polymorphic: true, index: true
t.timestamps null: false
end
end
end
I guess you already have the comments table, so you should rather change the table with
class ChangeComments < ActiveRecord::Migration
def change
change_table :comments do |t|
t.rename :post_id, :commentable_id
t.string :commentable_type, null: false
end
end
end
Also beware, that if you have live data you should update the commentable_type field of all already existing comments to Post. You can either do it in a migration or from the console.
Comment.update_all commentable_type: 'Post'
We don't need to use any new model, you can just refactor the current Comment model with polymorphic
So, a comment always belongs to a user, and belongs to a post or ad

counter_cache with has_many :through

I just created a counter_cache field and the controller looks like this.
#users = User.where(:sex => 2).order('received_likes_count')
The association in User.rb is
has_many :received_likes, :through => :attachments, :source => :likes, :dependent => :destroy
Problem is that counter_cache is declared in the belong_to of Like.rb and I don't know how to tell it that is for the has_many :through association.
belongs_to :user, :counter_cache => :received_likes
You have previous
class Product
has_and_belongs_to_many :categories
end
class Category
has_and_belongs_to_many :products
end
and migration
class CreateCategoriesProducts < ActiveRecord::Migration
def change
create_table :categories_products, id: false do |t|
t.references :category
t.references :product
end
add_index :categories_products, [:category_id, :product_id]
end
end
now change all to
class Product
has_many :categories_products, dependent: :destroy
has_many :categories, through: :categories_products
end
class Category
has_many :categories_products, dependent: :destroy
has_many :products, through: :categories_products
end
and new one
class CategoriesProduct < ActiveRecord::Base
# this model uses table "categories_products" as it is
# column products_count is in the table "categories"
belongs_to :category, counter_cache: :products_count
belongs_to :product
end
According to this post (from last month) and this post (from 2008), it doesn't seem to be possible. However, the latter post does have code for a workaround (copy/paste'd from that link for your convenience, credit goes to DEfusion in the second link)
class C < ActiveRecord::Base
belongs_to :B
after_create :increment_A_counter_cache
after_destroy :decrement_A_counter_cache
private
def increment_A_counter_cache
A.increment_counter( 'c_count', self.B.A.id )
end
def decrement_A_counter_cache
A.decrement_counter( 'c_count', self.B.A.id )
end
end
(This is for a scheme where C belongs_to B, B belongs_to A, A has_many C :through => B
This basically does the same thing:
after_save :cache_post_count_on_tags
def cache_post_count_on_tags
tags.each {|t| tag.update_attribute(:posts_count, t.posts.size)}
end
And you need a posts_count column on tags, or whatever associations you have.

Resources