Parse data from many-to-many relationship in Ruby on Rails - ruby-on-rails

I want to create a function where user be able to report the post if the post content contains something that doesn't follow the guidelines.
What I have so far:
model/post.rb
class Post < ApplicationRecord
belongs_to :author, class_name: 'User', foreign_key: 'author_id'
has_many :reports, dependent: :destroy
scope :reported, -> { distinct.joins(:reports) }
validates :title, :content, :author_id, presence: true
end
model/user.rb
class User < ApplicationRecord
has_many :posts, dependent: :destroy, foreign_key: "author_id"
has_many :reports, dependent: :destroy
end
model/report.rb
class Report < ApplicationRecord
belongs_to :user
belongs_to :author, class_name: 'User', foreign_key: "author_id"
belongs_to :post
validates :content, :user_id, :post_id, :author_id, presence: true
end
When the user trying to report the post, user will need to input content of the report and the data will be save on the Report Model.
This is my Report structure:
class CreateReports < ActiveRecord::Migration[5.2]
def change
create_table :reports do |t|
t.text :content
t.references :user
t.references :author
t.references :post, foreign_key: true
t.timestamps
end
end
end
I managed to store the data to the Report model/table.
So, in my dashboard_controller, I would like the author of the post noticed that they has some posts that being reported by the user that needs some action/attention from their side (edit or anything)
controller/dashboard_controller.rb
class DashboardController < ApplicationController
def report
#reports = current_user.posts.reported
#render json: #reports
end
end
The result will display the list of posts that user reported.
My questions:
How do I display content of the report from reports table?
I added #reports = current_user.posts.joins(:reports).select("posts.*, reports.*") in the dashboard_controller, it display the results that I wanted in json, but when I try to iterate like below, it doesn't display anything on views, no errors.
<% #reports.each do |report| %>
<h1><% report.content %></h1>
<% end %>
As far as I know, calling .joins will do SQL queries (N+1) instead of .includes. I didn't managed to get it working using .includes. Is it fine from what I've done?
Or is there any better solutions to what I've done now?
Thanks!

Related

Incorrect key when trying to create item on polymorphic association

I have a polymorphic association discussions using the following model:
class CreateDiscussions < ActiveRecord::Migration[5.2]
def change
create_table :discussions do |t|
t.references :organization, foreign_key: true
t.references :discussable, polymorphic: true
t.references :content, foreign_key: true
t.timestamps
end
end
end
and the model is defined as
class Discussion < ApplicationRecord
belongs_to :organization
belongs_to :discussable, polymorphic: true
has_one :content
end
which creates the columns discussable_id and discussable_type as I would expect.
The other side of the association is defined as
module Concerns
module Discussable
extend ActiveSupport::Concern
included do
has_many :discussions, as: :discussable, dependent: :destroy
end
end
end
when I try to create a discussion on my discussable I get the following error.
it 'can be added to a feature' do
expect(feat.discussions).to be_empty
Discussion.create(discussable: feat, content: content)
Which errors with:
Failure/Error: Discussion.create(discussable: feat, content: content)
ActiveModel::MissingAttributeError:
can't write unknown attribute `discussion_id`
Rails version 5.2.3
Ugggg.... I was all caught up in the polymorphic association I didn't notice my content model didn't have a foreign key for the discussion.
PEBKAC error.
In other words, I looked completely past the fact that
class Discussion < ApplicationRecord
belongs_to :organization
belongs_to :discussable, polymorphic: true
has_one :content
end
requires that content have the discussion_id column.

How do I access the extra attribute on join table in my show page?

My models look like this:
class Project < ApplicationRecord
has_many :comments
has_many :contractor_projects
has_many :contractors, through: :contractor_projects
validates_presence_of :title, :contract_number, :category, :project_start_date, :project_end_date, :substantial_completion_date, :category, :solicitation_number, :project_officer, :location
accepts_nested_attributes_for :contractor_projects
end
class Contractor < ApplicationRecord
has_many :contractor_projects
has_many :projects, through: :contractor_projects
validates :name, presence: true
validates :email, presence: true, uniqueness: true
end
class ContractorProject < ApplicationRecord
belongs_to :contractor
belongs_to :project
end
The ContractorProject model has an extra attribute #bid_status that I want to reflect on project's show page but it does not appear even though it's in the params when i raised it.
below is sample method for your case
def show
#project = Project.find(params[:id]
#contractors = #project.contractors
end
inside show.html.erb, you have to loop it, since it may get more than one records
<% #contractors.each do |contractor| %>
<%= contractor.bid_status %>
<% end %>

About rails polymorphic

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.

Ruby array validate based on join table foreign key [duplicate]

This question already has an answer here:
Need help on join table, limiting results to only the resource ID
(1 answer)
Closed 8 years ago.
I have a data model that looks like so;
class Tran < ActiveRecord::Base
belongs_to :buyer
validates :buyer_id, presence: true
belongs_to :seller
validates :seller_id, presence: true
end
class Seller < ActiveRecord::Base
has_many :trans
has_many :buyers, through: :trans
validates :seller_id, presence: true
end
class Buyer < ActiveRecord::Base
has_many :trans, :foreign_key => "buyer_id"
has_many :sellers, through: :trans
validates :buyer_id, presence: true
end
And then on each Seller page I have the following code that I can successfully give a list of the seller's top Buyers descending based on their total spend.
<ol>
<% #seller.buyers.uniq{|t| t.buyer_id }.sort_by {|su| su.trans.sum(:sum)}.reverse.each do |su| %>
<li><%= su.name %> <%= su.trans.sum(:sum) %></li>
<% end %>
</ol>
This code works in that it validates only Buyers that that have bought a product from the Seller, however it does not currently validate only the Trans made with the Seller (e.g. pulls in data between ALL the Sellers).
I have tried putting in a second validation around seller.id too, but unsure how to it.
Does anyone have a solution?
So essentially the list needs to validate that on the Tran, both the seller_id is owned by the Seller, plus present in the Trans join table, and that it also pulls in the buyer_id, but only the transactions that both supplier_id and buyer_id are present.
Rather than getting the Seller and the Seller's Buyers, and then the Buyer's Trans, how about getting the Seller and the Seller's Trans(es), and then the Buyer's information from each Seller's Trans?
Remove the unwanted code..its just pure has many through association...it should be
class Buyer< ActiveRecord::Base
has_many :trans
has_many :sellers, through: :trans
validates_presence_of :sellers
##or you can use blocks also such as below for validation(not null)
has_many :sellers,through :trans,:reject_if => proc { |a| a['name'].blank? }
end
class Tran< ActiveRecord::Base
belongs_to :buyers
belongs_to :sellers
end
class Seller< ActiveRecord::Base
has_many :trans
has_many :buyers, through: :trans
validates_presence_of :buyers
end
###to get all sellers of a buyer
#buyer.sellers
##to get all buyers from a seller
#seller.buyers
To make this work..you need to have three migration files including trans table with just two column-buyer_id and seller_id which will prevent any fallback in joins between buyer/seller and hence you dont need to validate the buyer_id/seller_id
class CreateBuyersSellersTrans < ActiveRecord::Migration
def change
create_table :buyers do |t|
##ensures uniqueness
t.string :name, unique: true
t.timestamps
end
create_table :sellers do |t|
##ensures uniqueness
t.string :name, unique: true
t.timestamps
end
create_table :trans do |t|
t.belongs_to :buyer
t.belongs_to :seller t.timestamps
end
end
end
in your controller
#get all uniq buyers of a seller in array format removing empty even if exits
#sellers=#seller.buyers.flatten.compact.uniq.delete_if(&:blank?)
in your view
<ol>
<% #sellers.sort_by {|su| su.trans.sum(:sum)}.reverse.each do |su| %>
<li><%= su.name %> <%= su.trans.sum(:sum) %></li>
<% end %>
</ol>

How to test/add data with associations in rails console

I'm a rails beginner and I'm having a little trouble testing model associations in the Rails Console. I know this is a simple fix, however, I'm not sure that I've created the appropriate models, or generated the right migrations, so I will include this information in this question.
I've read rails documentations (http://guides.rubyonrails.org/active_record_basics.html , http://api.rubyonrails.org/), I know the answer is in there, but i'm afraid my lack of experience is preventing me from totally maximizing the available documentation.
I need to be able to create an article that has_many :categories, through: :article_categories in the rails console
Here is the model for article , article_categories, and category
class Article < ActiveRecord::Base
belongs_to :user
has_many :article_categories
has_many :categories, through: :article_categories
validates :title, presence: true
validates :content, presence: true
validates :categories, presence: true
end
class ArticleCategory < ActiveRecord::Base
belongs_to :article
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :article_categories
has_many :articles, through: :article_categories
validates :names, presence: true
end
And here are the main migrations generated
Class CreateArticles < ActiveRecord::Migration
def change
create_table :articles do |t|
t.string :title
t.text :context
end
end
end
class CreateArticleCategories < ActiveRecord::Migration
def change
create_table :article_categories do |t|
t.belongs_to :article
t.belongs_to :category
t.timestamps
end
end
end
class CreateCategories < ActiveRecord::Migration
def change
create_table :categories do |t|
t.string :name
t.timestamps
end
end
end
So, again, the question is if all models and migrations are appropriately generated. How would I go about testing it in the rails console by adding an Article that has a Categorythrough the ArticleCategories table.
As of right new I've tested adding a new Category which works and adding an Article Category, which also works, but I do not know how to add the associations when I create an Article.
I've used
Article.errors.full_messages
which clearly tells me that I need to add categories before saving the article, but again, I don't know how to do that.
Thanks in advance!
You can use .build
#article = Article.new(:title => "foo")
#category = #article.categories.build(:name => "bar")
#article.save
##article & #category should now have been created
Or make them seperately and join them afterwards.
#article = Article.create(:title => "foo")
#category = Category.create(:name => "bar")
#article.categories << #category
#article = Article.create(:title => "foo")
#article = Article.create(:title => "foo")
#article.categories << #category

Resources