I am building a blog application in which I am trying to have two links in that two links I am sending the post_id to my update controller but it is giving me error .And I want to collect that post_id in my update controller and want to check the "status"(Status is basically the column name in my posts table and by default my status column has a status of pending ) .So when admin click on approve the status of column should change to approve and when admin click on decline the status of column should change to decline also user post should be deleted .Admin can access all the users posts whereas user can access his only post
posts_controller
class PostsController < ApplicationController
before_action :authenticate_user!
def index
#posts = Post.user_post(current_user).order('created_at DESC').paginate(:page => params[:page], :per_page => 5)
end
def new
#post = Post.new
end
def show
#post=find_params
end
def create
#post = Post.new(post_params)
#post.user = current_user
if #post.save
Post.upload(params[:post][:files],#post.id)
redirect_to #post
else
render 'new'
end
end
def edit
#post = find_params
puts "cccccccccc#{params[:commit]}"
Post.up(#post.id,params[:commit])
end
def update
#post = find_params
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post = find_params
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :body)
end
def find_params
Post.find(params[:id])
end
end
posts/_form.html.erb
<%= form_for #post,html: { multipart: true } do |f| %>
<% if #post.errors.any? %>
<div id="errors">
<h2><%= pluralize(#post.errors.count, "error") %> prevented this post from saving:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label :title %><br>
<%= f.text_field :title %><br>
<br>
<%= f.label :body %><br>
<%= f.text_field :body %><br>
<br>
<!-- if you want to upload multiple files at a time -->
<%= f.label :files %><br>
<%= f.file_field :files,:multiple => true %><br>
<br>
<%= f.submit %>
<br>
<% end %>
posts/edit.html.erb
<div id="page_wrapper">
<h1>Edit Post</h1>
<%= render 'form' %>
<br>
</div>
migration
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :title
t.text :body
t.integer :user_id
t.string :status
t.timestamps
end
end
end
posts/show.html.erb
<div id="post_content">
<h1 class="title"><%= #post.title %></h1>
<p class="date">
Submitted <%= time_ago_in_words(#post.created_at) %> Ago
<% if user_signed_in? %>
<%= link_to 'Edit', edit_post_path(#post) %> |
<%= link_to 'Delete', post_path(#post), method: :delete, data: { confirm: 'Are you sure?' } %>
<%= link_to "approve",[:edit,#post] %>
<%= link_to "decline",[:edit,#post] %>
<% end %>
</p>
<p class="body"><%= #post.body %></p>
<div id="comments">
<h2><%= #post.comments.count %> Comments</h2>
<%= render #post.comments %>
<h3>Add a comment:</h3>
<%= render "comments/form" %>
</div>
</div>
post.rb
class Post < ActiveRecord::Base
has_many :documents
has_many :comments, dependent: :destroy
belongs_to :user
validates :title, presence: true, length: {in: 5..15}
validates :body, presence: true,length: {in: 5..200}
def self.up(id,params)
puts "aaaaaaaa#{id}"
puts "bbbbbbbbbbbbb#{params}"
end
def self.user_post(id)
role = User.find_role(id)
if role == 'admin'
Post.all
elsif role == 'user'
Post.where(user_id: id)
elsif role == 'developer'
Post.where(user_id: id)
end
end
def self.icon(extension)
case extension
when 'pdf'
EXTENSION[1]['pdf']
when 'png' || 'jpg' || 'jpeg'
EXTENSION[0]['png']
when 'doc' || 'odt'
EXTENSION[2]['doc']
end
end
####limit to upload files not more than ######
def self.upload(files,post_id)
files.each do |file|
#file_extension=file.content_type.split('/')[1]
doc = Document.new(document: file,post_id: post_id )
#save is a method which will save the content in the database
doc.save!
end
end
end
routes.rb
Rails.application.routes.draw do
root "posts#index"
devise_for :users
resources :posts do
resources :comments
end
resources :uploads
end
There are a bunch of problems with your code.. I'll not try to address them all but the specific problem you are facing..
first you need to generate path for both approve and decline action..
resources :posts do
patch '/approve' => 'posts#approve', as: :approve #posts_approve_path(post) is the route helper for this
patch '/decline' => 'posts#decline', as: :decline #posts_decline_path(post) is the route helper for this
resources :comments
end
this will generate the required routes.
now inside your view wherever you want to approve and decline.
<%= link_to "approve",posts_approve_path(#post.id), method: :patch %>
<%= link_to "decline",posts_decline_path(#post.id), method: :patch %>
In your controller you need to modify find_params as the generated routes will pass :posts_id as the :id param.
def find_params
id = params[:id] || params[:posts_id]
Post.find(id)
end
Now in your controller add 2 new methods to implement the functionality.
def approve
#post = find_params
#post.update_attribute(:status, 'approved') if #post.present?
redirect_to post_path(#post), notice: 'Post approved'
end
def decline
#post = find_params
#post.destroy if #post.present?
redirect_to post_path(#post), notice: 'Post deleted'
end
Related
I have one question about form_for with nested resources. I create something like blog, with posts,comments, comments on comments(like replies).And I have an issue. Then I try to make comment it: "Redirected to http://localhost:3000/
Filter chain halted as :get_parent rendered or redirected
Completed 302 Found"
new.html.erb for comments:
<div class= "container" %>
<%= form_for #comment do |f| %>
<%= f.input :title %>
<%= f.text_area :body %>
<%= f.submit %>
<% end %>
</div>
My comments controller:
before_filter :get_parent
def new
#comment = #parent.comments.build
end
def create
#comment = #parent.comments.build(params[:comment])
#comment.user_id = current_user.id
if #comment.save
redirect_to posts_path(#comment.post), :notice => 'Thank you for your comment!'
else
render :new
end
end
private
def comment_params
params.require(:comment).permit(:body, :title, :user_id, :commentable_id, :commentable_type)
end
def get_parent
#parent = Post.find_by_id(params[:post_id]) if params[:post_id]
#parent = Comment.find_by_id(params[:comment_id]) if params[:comment_id]
redirect_to root_path unless defined?(#parent)
end
end
post model:
has_many :comments, as: :commentable
belongs_to :user
def post
commentable.is_a?(Post) ? commentable : commentable.post
end
comment model:
belongs_to :user
belongs_to :commentable, polymorphic: true
has_many :comments, :as => :commentable
routes:
resources :posts do
resources :comments
end
resources :comments do
resources :comments
end
post_show.html.erb
<h1><%= #post.title %></h1>
<div class="body">
<%= #post.body %>
</div>
<h2>Comments</h2>
<p><%= link_to 'Add a Comment', new_post_comment_path(#post) %></p>
<ul class="comment_list">
<%= render :partial => 'comments/comment', :collection => #post.comments %>
</ul>
github repo with app: https://github.com/Dmitry96/dasasd
Your new form does not pass neither post_id nor comment_id parameter. It should be eather in the form action url or in the form body.
I can not see all the picture, but I think you have to add parent id to the form action url. It is /comments now, has no parent id parameter in it. It must be /posts/:post_id/comments or /comments/:comment_id/comments.
Change your form to:
<%= form_for [#parent, #comment] do |f| %>
<%= f.input :title %>
<%= f.text_area :body %>
<%= f.submit %>
<% end %>
Im trying to add comments to my topics model the same way you can add comments to posts on my app. i currently have to partials for comments _comment.html.erb and _form.html.erb
_comment :
<%= content_tag :div, class: 'media', id: "comment-#{comment.id}" do %>
<div class= "media">
<div class= "media-body">
<small>
<%= comment.user.name %> commented <%= time_ago_in_words(comment.created_at) %> ago
<% if user_is_authorized_for_comment?(comment) %>
| <%= link_to "Delete", [comment.post, comment], method: :delete %>
<% end %>
</small>
<p> <%= comment.body %></p>
</div>
</div>
<% end %>
_form :
<h4>Add a comment</h4>
<%= form_for [post, comment] do |f| %>
<div class="form-group">
<%= f.label :body, class: 'sr-only' %>
<%= f.text_field :body, class: 'form-control', placeholder: "Enter a new comment" %>
</div>
<%= f.submit "Submit Comment", class: 'btn btn-default pull-right' %>
<% end %>
my topic show is :
#DISPLAY Topic comments here
<h3> Comments</h3>
<%= render #topic.comments %>
</div>
<% if current_user %>
<%= render 'comments/form', comment: Comment.new, post: #post %>
<% end %>
#------
comment controller :
def create
#post = Post.find(params[:post_id])
comment = #post.comments.new(comment_params)
comment.user = current_user
if comment.save
flash[:notice] = "Comment saved successfully."
redirect_to [#post.topic, #post]
else
flash[:alert] = "Comment failed to save."
redirect_to [#post.topic, #post]
end
end
def destroy
#post = Post.find(params[:post_id])
comment = #post.comments.find(params[:id])
if comment.destroy
flash[:notice] = "Comment was deleted"
redirect_to [#post.topic, #post]
end
end
i have updated the routes for topic comments :
resources :topics do
resources :posts, except: [:index]
resources :comments, only: [:create, :destroy]
end
my question is do i need to create a separate partial to add comments to topics or can i update my _comment partial to work for both post and topic comments . and how can i accomplish this ?
Models
You'll need a polymorphic association on the Comment model:
#app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
#app/models/topic.rb
class Topic < ActiveRecord::Base
has_many :comments, as: :commentable
end
#app/models/post.rb
class Post < ActiveRecord::Base
has_many :comments, as: :commentable
end
Controllers
This will allow you to save the comments for your various models, the controllers / flow coming secondary:
#config/routes.rb
resources :topics, :posts do
resources :comments, only: [:create, :destroy] #-> url.com/topics/:topic_id/comments
end
#app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def create
id = params[:post_id] || params[:topic_id]
if params[:post_id]
#parent = Post.find id
elsif params[:topic_id]
#parent = Topic.find id
end
#comment = #parent.comments.find params[:id]
#comment.save
end
def destroy
#parent = params[:post_id] || params[:topic_id]
#comment = #parent.comments.new comment_params
#comment.destroy
end
private
def comment_params
params.require(:comment).permit(:x, :y)
end
end
Because you're passing the data to the comments controller, you'll only need to evaluate which #parent you're working with.
Views
For your views, you need to pass locals to your _form partial:
#app/views/posts/show.html.erb
<%= render "comments/form", locals: {parent: #post} %>
#app/views/comments/_form.html.erb
<%= form_for [parent, parent.comments.new] do |f| %>
I had this road-block as well and here is what I came up with that passed. I must first give #Richard Peck applause for getting my wheels turning, so thank you :).
Models
Did not implement a polymorphic association. Stuck with has_many and belongs_to, nothing more
Partials
_comment.html.erb
Set up the delete partial to accept "parent" as a local
<div class="media">
<div class="media-body">
<small>
<%= comment.user.name %>
commented
<%= time_ago_in_words(comment.created_at) %>
ago
<% if user_is_authorized_for_comment_via_post?(comment) %>
|
<%= link_to "Delete", [parent, comment], method: :delete %>
<% end %>
</small>
<p>
<%= comment.body %>
</p>
</div>
</div>
_form.html.erb
same idea as _comment.html.erb, see above
<h4>Add a comment</h4>
<%= form_for [parent, comment] do |f| %>
<div class="form-group">
<%= f.label :body, class: 'sr-only' %>
<%= f.text_field :body, class: 'form-control', placeholder: "Enter a new comment" %>
</div>
<%= f.submit "Submit Comment", class: 'btn btn-default pull-right' %>
<% end %>
Setting up CommentController
...
def create
# upon clicking on create, determine what param id is passed
if params[:post_id]
# if it is a post id, set instance of post id as #parent
#parent = Post.find(params[:post_id])
elsif params[:topic_id]
# if it is a topic id, set instance of topic id as #parent
#parent = Topic.find(params[:topic_id])
end
# create instance as #comment. Build/create
# comment belonging to #parent (Topic or Post)
#comment = #parent.comments.build(comment_params)
# The comment must be associated to the current user.
# A comment must have a user, and value of user within instance of #comment
# is currently nil. Set user id as current user
#comment.user = current_user
# save comment to database
if #comment.save
# direction of save through if and elsif
# Redirection depends on the comment's parent.
# .is_a? method determines if it is of a certain class. Here, is #parent
# of class Post? Is #parents is the same parent id passed through params?
if #parent.is_a?(Post) # template error with this included: (== params[:post_id])
flash[:notice] = 'Comment saved successfully'
redirect_to [#parent.topic, #parent]
# if not part of the class Post, is it a Topic? If so, save here and
# redirect to the topic after save
elsif #parent.is_a?(Topic)
flash[:notice] = 'Comment saved successfully'
redirect_to #parent
end
end
end
def destroy
comment = Comment.find(params[:id])
# #topic = Topic.find(params[:topic_id])
# topic_comment = #topic.comments.find(params[:id])
# #post = Post.find(params[:post_id])
# post_comment = #post.comments.find(params[:id])
if comment.destroy
flash[:notice] = 'Comment was deleted'
redirect_to :back
else
flash[:alert] = "Comment counld't be deleted. Try again"
redirect_to :back
end
end
...
Passing in Comments from topic/show and post/show
topic/show
Note: notice how locals are passed into the controller from here
...
<div class="row">
<%= render 'comments/form', comment: Comment.new, parent: #topic %>
</div>
<% #topic.comments.each do |comment| %>
<%= render partial: 'comments/comment', locals: { parent: #topic, comment: comment } %>
<% end %>
...
post/show
<% if current_user %>
<% #post.comments.each do |comment| %>
<%= render partial: 'comments/comment', locals: { parent: #post, comment: comment } %>
<% end %>
<% end %>
<% if current_user %>
<%= render 'comments/form', comment: Comment.new, parent: #post %>
<% end %>
Hope this helps.
I have a many_to_many association between Categories and Articles, using has_and_belongs_to_many in a Rails 4 app:
Here are the corresponding migration and classes:
class CategoriesArticles < ActiveRecord::Migration
def change
create_table :categories_articles, id: false do |t|
t.belongs_to :category, index: true
t.belongs_to :article, index: true
end
add_index :categories_articles, [:category_id, :article_id]
end
end
class Category < ActiveRecord::Base
has_and_belongs_to_many :articles
end
class Article < ActiveRecord::Base
has_and_belongs_to_many :categories
end
When a user creates a new article, I simply want to give him or her the option to select categories that he/she wants to associate with the new article. I want the user to be able to select these categories with checkboxes.
Here's the ArticlesController:
class ArticlesController < ApplicationController
before_action :set_article, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, only: [:new, :create, :edit, :destroy, :update]
before_action :verify_own_article, only: [:destroy]
respond_to :html
def index
if user_signed_in?
#articles = current_user.article_feed
# TODO: if there are too few articles on a user's feed, we want to display more articles
else
#articles = Article.all
end
#articles = #articles.page(params[:page] || 1).per(12)
respond_with(#articles)
end
def show
respond_with(#article)
end
def new
#categories = Category.all
#article = Article.new
respond_with(#article)
end
def edit
if current_user.articles.find_by_id(params[:id]).nil?
flash[:notice] = "You do not have permission to edit this article."
redirect_to #article
end
end
def create
# Creates article object with current_user_id, initial_comment, and URL
#article = current_user.articles.build(article_params)
# Uses Pismo (gem) to grab title, content, photo of URL
#article.populate_url_fields
if #article.save
flash[:success] = "Article created!"
# Might need to change the location of this redirect
redirect_to root_url
else
flash[:notice] = "Invalid article."
redirect_to new_article_path
end
end
def update
#article.update(article_params)
flash[:notice] = "Article successfully updated."
respond_with(#article)
end
def destroy
if #article
#article.destroy
flash[:notice] = "Article successfully destroyed."
else
flash[:notice] = "You do not have permission to delete this article."
end
# TODO: change this to another redirect location
redirect_to root_path
end
private
def set_article
#article = Article.find(params[:id])
end
def article_params
params.require(:article).permit(:url, :title, :datetime, :content, :photo, :initial_comment)
end
# Ensure that a signed in user can only delete articles that they have posted
def verify_own_article
#article = current_user.articles.find_by_id(params[:id])
end
end
Here's the article new.html.erb view:
<h1>New article</h1>
<%= render 'form' %>
<%= link_to 'Back', articles_path %>
... and the form partial:
<%= form_for(#article) do |f| %>
<% if #article.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#article.errors.count, "error") %> prohibited this article from being saved:</h2>
<ul>
<% #article.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :url %><br>
<%= f.text_field :url %>
</div>
<div class="field">
<%= f.label :initial_comment %><br>
<%= f.text_field :initial_comment %>
</div>
<% #categories.each do |t| %>
<div class="field">
<%= f.label t.name %>
<%= f.check_box "categories[#{t.id}]" %>
<br />
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
However, this is erroring for me, specifically the lines:
<% #categories.each do |t| %>
<div class="field">
<%= f.label t.name %>
<%= f.check_box "categories[#{t.id}]" %>
<br />
</div>
<% end %>
Specifically, it's telling me:
undefined method 'categories[1]' for #<Article:0x007f401193d520>. How do I fix this? Thanks.
I note your join table is named
create_table :categories_articles, id: false do |t|
The name needs to be in alphabetical order.
create_table :articles_categories, id: false do |t|
My pick is that Rails cannot find your join table. This means that the form when it calls #article.categories cannot find what it needs. The same will happen if you call #category.articles (ie: being able to find the join table is not related to the arbitrary order of the objects).
See my answer to this question
I was following this tutorial http://www.sitepoint.com/nested-comments-rails/ to implement nested comments for an image board. It worked fine until I made "comments" belong to "boards" and then had to nest my routes.
Here are my routes:
Rails.application.routes.draw do
root "boards#index"
devise_for :users do
get '/users/sign_out' => 'devise/sessions#destroy'
end
resources :boards do
resources :comments
get '/comments/new/(:parent_id)', to: 'comments#new', as: :new_comment
get '/comments/(:parent_id)', to: 'comments#destroy', as: :delete_comment
get '/comments/edit/(:parent_id)', to: 'comments#edit', as: :edit_comment
end
end
Here is my form:
<%= form_for [#board, #comment] do |f| %>
<% if #comment.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#comment.errors.count, "error") %> prohibited this comment from being saved:</h2>
<ul>
<% #comment.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.hidden_field :parent_id %>
<div class="form-group">
<% if #comment.parent_id == nil %>
<%= f.label :title %>
<%= f.text_field :title, class: 'form-control' %>
<% else %>
<% nil %>
<% end %>
</div>
<div class="form-group">
<%= f.radio_button(:user_id, current_user.id) %>
<%= f.label(:user_id, "I want to post as myself") %>
<%= f.radio_button(:user_id, nil) %>
<%= f.label(:user_id, "I want to post anonymously") %>
</div>
<div class="form-group">
<%= f.label :content %>
<%= f.text_area :content, class: 'form-control', required: true %>
</div>
<div class="form-group">
<%= f.label :image %>
<%= f.file_field :image %>
</div>
<%= f.submit class: 'btn btn-primary' %>
<% end %>
And here is my controller:
class CommentsController < ApplicationController
def index
#comments = Comment.hash_tree
end
def new
#comment = Comment.new(parent_id: params[:parent_id])
end
def edit
#comment = Comment.find(params[:parent_id])
end
def create
if params[:comment][:parent_id].to_i > 0
parent = Comment.find_by_id(params[:comment].delete(:parent_id))
#comment = parent.children.build(comment_params)
else
#comment = Comment.new(comment_params)
end
if #comment.save
redirect_to root_url
else
render 'new'
end
end
def update
#comment = Comment.find(params[:id])
if #comment.update(comment_params)
redirect_to #comment
else
render 'edit'
end
end
def make_parent
#comment.parent_id = nil
#comment.save
end
def destroy
#comment = Comment.find(params[:parent_id])
#comment.destroy
respond_to do |format|
format.html { redirect_to comments_url }
end
authorize! :destroy, #comment
end
private
def comment_params
params.require(:comment).permit(:title, :content, :user_id, :image)
end
end
I've tried setting a custom route in the form, this gets the form to at least appear, however when you hit the submit button it returns 'No route matches [POST] "/boards/1/comments/new"'. If I got to the controller and then change the corresponding "get" to a "post" then it causes the form to just reappear after pressing submit and nothing is added to the database. I've also experimented with shallow nesting my routes as per my instructors advice but this didn't work.
Your association between boards and comments must be:
board.rb
has_many :comments
comment.rb
belongs_to :user
routes.rb
resources :boards do
resources :comments, only: [:new, :edit, :destroy]
end
this will create a route
new_board_comment GET /boards/:board_id/comments/new(.:format) comments#new
edit_board_comment GET /boards/:board_id/comments/:id/edit(.:format) comments#edit
board_comment DELETE /boards/:board_id/comments/:id(.:format) comments#destroy
I've created a small app with a bar to post messages and I want to add a delete button to every message so I can delete it.
This is my code:
Controller:
class HomeController < ApplicationController
def create
#message = Message.new(params[:message])
if #message.save
redirect_to "/home/show_msg"
else
render :action => "/home/home_page"
end
end
def home_page
#message = Message.new
end
def show_msg
#postwords = Message.all
end
def index
end
end
View (home_page):
<center><h1>MiNibloG</h1></center>
<br>
<br>
<div align="center">
<%= form_for #message, :url => "/home/create" do |f| %>
<%= f.label :postword %>
<%= f.text_field :postword %>
<%= f.submit "Post" %>
<% end %>
</div>
View (show_msg):
<center>
<h1>Posts list</h1>
<br>
<br>
<ul>
<% #postwords.each do |p| %>
<%= p.postword %><br><br>
<% end %>
</ul>
</center>
And this is the migration:
class CreateMessages < ActiveRecord::Migration
def self.up
create_table :messages do |t|
t.string :postword
t.timestamps
end
end
def self.down
drop_table :messages
end
end
Please take a look at the scaffolding, scaffold fits ideally for you situation.
rails generate scaffold message postwords:string
This command generate for you views, controller and migration.
For deleting with your own non REST controller, use something like code below(didnt test it)
<% #postwords.each do |p| %>
<%= p.postword %>
<%= link_to 'Destroy', destroy(p) %><br>
<% end %>
in controller:
def destroy
#message = Message.find(params[:id])
#message.destroy
respond_to do |format|
format.html { redirect_to some_route }
format.xml { head :ok }
end
end
add destroy action in the routes.rb