I have a rails form that I want to use to edit an answer by running the update action (PATCH), but I keep getting this error:
No route matches [POST] "/answers/724"
This is the form:
<%= form_for :answers, url: { action: :update } do |f| %>
<%= f.label(#answer.question.question_text) %>
<%= f.text_area(#answer.answer_text) %>
<div class="form-group">
<%= f.submit "Submit", class: "btn btn-success" %>
</div>
<% end %>
Controller:
def edit
#answer = Answer.find_by(id: params[:id])
end
def update
#answer = Answer.find_by(id: params[:id])
if #answer.update(answer_text: params[:answer][:answer_text])
redirect_to '/answers/edit'
flash[:success] = "Answer Updated"
else
render 'edit'
end
end
How can I get this form to perform a PATCH request and not POST?
you need to explicitly pass the method to form_for
form_for :answers, url: { action: :update }, html: { method: :patch } do |f|
Related
I have topics and posts and they both have relationships. I've been following the guide on how to add a nested post but when i try to reply to a post i get Couldn't find Topic with 'id'=.
In my Posts controller
def create
#topic = Topic.find params[:topic_id]
#post = Post.new(post_params)
#post.user_id = current_user.id
#post.topic_id = #topic.id
if #post.save
redirect_to #topic
else
render :new
end
end
In my Topics controller
def show
#topic = Topic.find params[:id]
#post = Post.new(:parent_id => params[:parent_id])
#posts = #topic.posts
#topic.punch(request)
end
I'll keep it short for how i have my reply button in my topics/show.html.erb page
<% #posts.each do |post| %>
<%= link_to "Reply", new_topic_post_path(#topic, :parent_id => post) %>
<% end %>
Now this is my form
<%= simple_form_for [#topic, #post] do |f| %>
<%= f.hidden_field :parent_id %>
<%= f.input :content, as: :pagedown, input_html: { preview: true, rows: 10 }, label: 'Markdown' %>
<%= f.submit "Post", class: 'button expanded' %>
<% end %>
#Kristján Answered my question. I was missing params[:topic_id]
I'm trying to solve a really simple problem with my code. I want to upload image into a post, I use paperclip, and the last step is not working.
That is my controller :
class PostsController < ApplicationController
def index
#posts = Post.all
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
flash[:success] = "uccess!"
redirect_to post_path(#post)
else
flash[:error] = #post.errors.full_messages
redirect_to new_post_path
end
end
def show
#post = Post.find(params[:id])
end
private
def post_params
params.require(:post).permit(:title, :image, :prix, :adress, :description)
end
end
that my form :
<%= simple_form_for #post, url: root_path do |f|%>
<%= f.input :title, label: "Nom du plat" %>
<br>
<%= f.input :image, as: :file %>
<br>
<%= f.input :prix %>
<%= f.input :adress, label: "Localisation" %>
<%= f.input :description %>
<br>
<%= f.button :submit %>
<% end %>
and that my view :
<%= image_tag (#post.image.url(:medium)) %>
<br>
<%= #post.description %>
<br>
<button>
<%= link_to "Home", root_path %>
</button>
So if you go through, and you spot a stupid mistake, please let me know.
You're calling root_path as the helper in your form when you need to call the route for post. Run rake routes and look for the create action related to post, which should be the same as the get route for the show action.
This should not be root_path:
<%= simple_form_for #post, url: root_path do |f|%>
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 am having trouble getting error messages to display when a user tries to submit a blank comment. Upon create failure it render 'posts/show' properly but it doesnt appear to be sending the #comment object that my error_messages partial expects. Any thoughts?
comments_controller.rb:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(comment_params)
#comment.user = current_user
if #comment.save
flash[:success] = "Comment created!"
redirect_to post_path(#post)
else
#comments = #post.comments.paginate(page: params[:page], :per_page => 5)
render 'posts/show' # => Renders but does not display errors???
end
end
posts_controller.rb:
def show
#post = Post.find(params[:id])
#comments = #post.comments.paginate(page: params[:page], :per_page => 5)
#comment = #post.comments.build if signed_in?
end
_comment_form.html.erb
<%= form_for([#post, #post.comments.build], :html => { :role => "form" }) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="form-group">
<%= f.label :content %>
<%= f.text_area :content, class: "form-control", placeholder: "Enter new comment..." %>
</div>
<div class="btn-group">
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
</div>
<% end %>
<%= form_for([#post, #post.comments.build], :html => { :role => "form" }) do |f| %>
This always creates a form for blank comment object. So After create action, it does the same and that's why you are not getting any errors.
As you already done the initialization for the #comment in your create action, so you can use that in the form to get the error message for that #comment object. And I believe you have initialized the #comment object in your show action as well to work the partial form for both the show and create action.
So try using the following
<%= form_for([#post, #comment], :html => { :role => "form" }) do |f| %>
I'm relatively new to RoR, and have been working on a lesson-site sharing back-end.
Currently I have two models: Lesson and Revision. The idea is that a teacher would create a lesson (with a title, description, etc.), at the lesson/new page. The form would redirect to another form with other fields (content, comments) at the lesson/lesson_id/revision/new page. Eventually teachers will be able to "save and modify" lessons by copying revisions to their own profile, but the model isn't there yet.
I'm still focusing on two supposedly basic issues, both in creating new lessons and revisions.
1) Saving the lessons to a user, and revisions to a user and lesson. At one point I had revisions saved to users, but lessons were not saving.
2) Redirecting from the lesson form to the revision form, and the revision form to the revision page. I've tried changing the redirects in the controller and one of the form_for blocks, to no avail. The new lesson form still wants to go to /lessons rather than /lesson/1/revision/new, which I've tried to redirect to with new_lesson_revision_path. The revision form also just goes back to /revisions, which will be an admin index but I'd rather go to /lesson/1/revision/1 etc.
Thanks in advance for any advice on either or both of these issues!
Below is my code for reference. An excerpt from routes.rb:
resources :lessons
resources :revisions #For index pages for admin
resources :lessons do
resources :revisions
end
Models
class Lesson < ActiveRecord::Base
attr_accessible :stable, :summary, :title, :time_created
has_many :revisions, :class_name => "Revision"
has_many :users, :through => :revisions
end
class Revision < ActiveRecord::Base
attr_accessible :comment, :lesson_id, :user_id, :description, :content, :time_updated
belongs_to :lesson, :class_name => "Lesson", :foreign_key => "lesson_id"
belongs_to :user, :class_name => "User", :foreign_key => "user_id"
accepts_nested_attributes_for :lesson
end
Lessons
new.html.erb
<% if user_signed_in? %>
<h2>New lesson</h2>
<%= form_for #lesson do |f| %>
<%= render "form" %>
<% end %>
<%= link_to 'Back', lessons_path %>
<% else %>
<h3> Please <%= link_to 'sign in', new_user_session_path %> or <%= link_to 'create an account', new_user_registration_path %> to make and save lessons.</h3>
<% end %>
Lessons
_form.html.erb
<%= form_for #lesson, :url => new_lesson_revision_path(#lesson_id, #revision) do |f| %>
<div class="field">
<%= f.label :title %><br />
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :summary %><br />
<%= f.text_area :summary %>
</div>
<div class="field">
<%= f.label :stable %><br />
<%= f.check_box :stable %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Revisions
new.html.erb
<% if user_signed_in? %>
<h2>New revision</h2>
<%= form_for #revision do |f| %>
<%= render "form" %>
<% end %>
<%= link_to 'Back', lessons_path %>
<% else %>
<h3> Please <%= link_to 'sign in', new_user_session_path %> or <%= link_to 'create an account', new_user_registration_path %> to make and save revisions.</h3>
<% end %>
Revisions
_form.html.erb
<%= form_for(#revision) do |f| %>
<div class="field">
<%= f.label :description %><br />
<%= f.text_field :description %>
</div>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content %>
</div>
<div class="field">
<%= f.label :comment %><br />
<%= f.text_area :comment %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Lesson Controller: new/create
def new
#lesson = Lesson.new
end
def create
#user = current_user
#lesson = Lesson.new(params[:lesson])
##user.lessons << #lesson
params[:lesson][:user_id] = current_user.id
params[:lesson][:lesson_id] = #lesson.id
respond_to do |format|
if #lesson.save
format.html { redirect_to new_lesson_revision_path(params[:lesson_id]), notice: 'Your lesson was successfully created.' }
format.json { render json: #lesson, status: :created, location: new_lesson_revision_path}
else
format.html { render action: "new" }
format.json { render json: #lesson.errors, status: :unprocessable_entity }
end
end
end
Revisions Controller: new/create
def new
#revision = Revision.new
end
def create
#lesson = Lesson.find(params[:lesson_id])
#revision = #lesson.revisions.new(params[:revision])
#revision.user_id = current_user.id
#revision.lesson_id = #lesson.id
#revision.time_updated = DateTime.now
respond_to do |format|
if #revision.save
format.html { redirect_to current_user.profile }
format.json { }
else
render :new
end
end
end
Lastly here are most of the results from my rake routes:
home_index GET /home/index(.:format) home#index
profile POST /profile(.:format) profiles#create
new_profile GET /profile/new(.:format) profiles#new
edit_profile GET /profile/edit(.:format) profiles#edit
GET /profile(.:format) profiles#show
PUT /profile(.:format) profiles#update
DELETE /profile(.:format) profiles#destroy
lessons GET /lessons(.:format) lessons#index
POST /lessons(.:format) lessons#create
new_lesson GET /lessons/new(.:format) lessons#new
edit_lesson GET /lessons/:id/edit(.:format) lessons#edit
lesson GET /lessons/:id(.:format) lessons#show
PUT /lessons/:id(.:format) lessons#update
DELETE /lessons/:id(.:format) lessons#destroy
lesson_revisions GET /lessons/:lesson_id/revisions(.:format) revisions#index
POST /lessons/:lesson_id/revisions(.:format) revisions#create
new_lesson_revision GET /lessons/:lesson_id/revisions/new(.:format) revisions#new
edit_lesson_revision GET /lessons/:lesson_id/revisions/:id/edit(.:format) revisions#edit
lesson_revision GET /lessons/:lesson_id/revisions/:id(.:format) revisions#show
PUT /lessons/:lesson_id/revisions/:id(.:format) revisions#update
DELETE /lessons/:lesson_id/revisions/:id(.:format) revisions#destroy
GET /lessons(.:format) lessons#index
POST /lessons(.:format) lessons#create
GET /lessons/new(.:format) lessons#new
GET /lessons/:id/edit(.:format) lessons#edit
GET /lessons/:id(.:format) lessons#show
PUT /lessons/:id(.:format) lessons#update
DELETE /lessons/:id(.:format) lessons#destroy
root / home#index
/profile(.:format) profiles#show
Your question : 1) Saving the lessons to a user, and revisions to a user and lesson. At one point I had revisions saved to users, but lessons were not saving.
see on your lessons_form
<%= form_for #lesson, :url => new_lesson_revision_path(#lesson_id, #revision) do |f| %>
You are using new_lesson_revision_path action form, this is not saving a #lesson, change your lessons_form to :
<%= form_for #lesson do |f| %>
and lessons_controller.rb looks like :
def new
#lesson = Lesson.new
end
def create
#user = current_user
#lesson = Lesson.new(params[:lesson])
....
#lesson.save
# see, redirect_to new lesson with params[lesson_id]
redirect_to new_lesson_revision_path(params[:lesson_id])
end
and on revisions_controller.rb looks like :
def new
#revision = Revision.new
# get #lesson with params(:lesson_id)
#lesson = Lesson.find(params[:lesson_id])
end
def create
#revision = #revision.new(params[:revision])
#revision.user_id = current_user.id
#revision.time_updated = DateTime.now
respond_to do |format|
if #revision.save
format.html { redirect_to current_user.profile }
format.json { }
else
render :new
end
end
end
and on your revisions_form, you can add lesson_id with hidden_field
<%= form_for(#revision) do |f| %>
<%= f.hidden_field :lesson_id, :value => #lesson.id %>
..
..
<% end %>
no 2
Why should you not use nested form or multistep form?
If you are use nested form, you can make lessons_form and
revisons_form in one form.
see screencasts part
1 and
part 2
for nested form
If you are using multistep form, you can resolved you problem no 2
see screencasts Wizard Forms
Addtional
error: undefined method 'revisions' for nil:NilClass - from the line #revision = #lesson.revisions.new(params[:revision])
Because #revision is stand-alone on your case, you could change this
#revision = #lesson.revisions.new(params[:revision])
to
#revision = #revision.new(params[:revision])