CRUD of a model within another model - ruby-on-rails

I have Post CRUD. On show.html.erb for Post, I want CRUD for comment. I am using devise for User. I have tried what others have suggested elsewhere on stack overflow but it has not worked. It keeps saying #comment is empty. I'd appreciate your help.
comments controller
def new
#user = current_user
#post = Post.find(params[:post_id])
#comment = #post.comments.new
render :template => 'posts/show'
end
def create
#user = current_user
#post = Post.find(params[:post_id])
#comment = #post.comments.new(post_params)
render :template => 'posts/show'
if #comment.save
redirect_to post_path(post.id), notice: "Success!~"
else
redirect_to post_path(post.id), alert: "Failure!"
end
end
private
def comment_params
params.require(:comment).permit(:text)
end
show.html.erb (within posts folder)
<%= form_for([#user, #post, #comment]) do |form| %>
<p>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>

As far as I understand you, you want to have the CRUD actions for comments in the show view of a post. It's called nested routing or just nested models.
From you posts/show view I can see from your form for you also nested the posts inside the users. That's not necessary. As a general guideline, don't nest models more than one level. I guess in your app posts belong to a user, and you're using devise. So by the creation of a post you can just assign the current_user to the post and then you don't need to nest them inside each other.
Now for your questions: Since you have the form for a new comment on the show page of post, you need to have the code from your comments_controller#new in the posts_controller#show. Why? Because you use the instance variable #comment in the form_for. If in your posts_controller there is no such #comment defined,it will throw the error.
Once your form is then filled and submitted a post request to comments_controller#create is made, where you save the code and redirect to the post#show.

Related

Simple_form does not accept the instance variable from action create

I am creating a very simple StackOverflow type of website
written in Ruby on Rails. I created four actions in my questions controller and one of them is 'Create'.
I proceeded in the view page index.html.erb and I create a simple_form where I get input(question) from a user.
I get an error (NoMethodError in Questions#index..undefined method `[]' for nil:NilClass)
The question belongs to the current_user and I think that might be the problem. I thought maybe I need to initialize one more variable in order to get my form to work.
Can please someone tell me what am I missing here?
Thank you in advance!
class QuestionsController < ApplicationController
def index
#questions = Question.all
end
def show
#question = Question.find(params[:id])
end
def new
#question = Question.find(params[:user_id])
#question = Question.new
end
def create
#question = Question.new(accepted_params)
if #question.save
redirect_to questions_show_path, notice: 'Question submitted'
else
#question = Question.find(params[:id])
render :new
end
end
<%= simple_form_for #questions, #user do |f| %>
<%= f.input :title %>
<% f.input :content %>
<%= f.submit :submit, class: 'btn btn-secondary'%>
<% end %>
You should use the following code:
def create
#question = Question.new(accepted_params)
if #question.save
redirect_to question_path(#question), notice: 'Question submitted'
else
render :new
end
end
Let me explain:
on a create action there is no params[:id] (because we are creating a new item)
also: we established that saving failed so trying to retrieve it from the database would only make sense on an edit action
third: simple-form will look at the error-messages and incorporate them into the form, so the user can then fix the errors.
and lastly: I fixed your redirect_to to be more "rails"-like, but this depends on your route-definition. I am assuming you have something like resources :questions in your routes (but if you do not give a parameter that could also never work imho)
E.g. if you have a validate_presence_of :name in your model, this could cause a validation-error upon save, and then we could present the field in red in the form when rerendering.
[TYPO in form?]
Lastly, after your comment I noticed it said simple_form_for #questions, #user and that should be either be the singular simple_form_for #question. If you want to edit the question as a nested path for the user, I think the correct form is simple_form_for [#user, #question].

Rendering two partials with a form using ajax

I'm rendering a partial that renders a form, using ajax (I can't just render the form directly). Posting a comment works fine when I'm rendering the form without ajax but with ajax it seems the partial can't access the #post variable.
<%= link_to "Render form", submit_comment_path, :remote => true %>
<div id="form">
</div>
I have a submit_comment.js file that looks like this:
$("#form").html("<%= j render(partial: 'comment_partial', locals: {post: #post}) %>");
The comment_partial view:
<%= render 'comments/form' %>
The form view:
<%= simple_form_for [post, Comment.new] %>
...
The submit_comment_path route:
get '/submit_comment', to: 'posts#submit_comment', as: :submit_comment
The posts controller (it's being rendered on the show page):
def show
#post = Post.find(params[:id])
end
def submit_comment
respond_to do |format|
format.html
format.js
end
end
and the comments controller:
def create
#post = Post.find(params[:post_id])
end
If I try to post a new comment it gives me a routing error and takes me to /posts//comment. Putting post.id in the comment_partial gives me an undefined error.
The big piece of the puzzle is understanding that instance variables (#post in this instance) disappear as soon as the Controller renders anything.
You correctly assign #post when you render the show page:
def show
#post = Post.find(params[:id])
end
However, #post disappears the second that show.html.erb is done rendering. When you click on the link to hit the submit comment method, no #post is getting created...
def submit_comment
# No instance variables here! :(
respond_to do |format|
format.html
format.js
end
end
Which means that the submit_comment.js file has no idea which post to be generating a form for.
However, it's not as simple as just throwing another Post.find(params[:id]) into the submit_comment method. You need to:
Define a route that relies on post id
Change the link in show.html.erb to include a specific #post.id
Then find the corresponding post to create a comment for.
It might look something like this...
routes.rb
...
resources :posts do
member do
get 'submit_comment'
end
end
...
Read up on member routes at the Rails Guide. There are other ways of accomplishing a similar route.
posts/show.html.erb
<%= link_to "Render form", submit_comment_post_url(#post), :remote => true %>
Note that the Rails default url helper is different than the one you've got, if you use member routes.
posts_controller.rb
def submit_comment
#post = Post.find(params[:id])
...
end
Hope that helps! Happy form-ing!

Rails passing hidden form data via Controller

UPDATE: It seems like the error was probably due to a devise conflict. I used some content from another app which apparently made devise go nutty and not be able to read user sessions. I'll be starting with a clean install and seeing if I have better luck. Thanks!
I am creating a page which has a single question on it. On that page I have a form where people can answer that question. The form itself just has an answer block and a submit button. However, I want to tie it to both the user submitting the form and the question the answer is linked to. For security I want to do this in the controller, not the view. The answer form is being shown on the questions#index page. I am currently getting the following error:
undefined method `user' for #<AnswersController:0x007fe618e5cc10>
I suspect that if it made it past the user I would get the same for 'question'
The questions#index looks like this:
<div class="home_question">
<h1><%= #daily.question %></h1>
<div class="answer_form">
<%= form_for #answer do |answer| %>
<%= answer.label :answer, "What do you think?" %>
<%= answer.text_area :answer %>
<%= answer.button %>
<% end %>
</div>
</div>
The Questions Controller looks like this:
def index
#daily = Question.find_by(show_month: Time.now.month, show_day: Time.now.day)
#answer = Answer.new
end
The Answers Controller looks like this:
def index
#answers = Answer.all
#answer = Answer.new
end
def new
#answer = Answer.new
end
def create
#answer = Answer.new(answer_params)
#user = user(params[:id])
#question = question(params[:id])
if #answer.save
redirect_to root
else
render 'new'
end
end
private
def answer_params
params.require(:answer).permit(:answer, :users_id, :questions_id)
end
The data currently being passed by the form is:
{"utf8"=>"✓",
"authenticity_token"=>"mA16+dg7+Edzxvu/FVWR5r8PZ9zdNaOyvOwSz1VpOXU=",
"answer"=>{"answer"=>"test"},
"button"=>""}
The error that you're getting is because in the controller you're using the user variable which isn't defined. You want to use the uppercase model name User.
Getting the user id
If this is a logged in user then you can get their id by looking in the session. Usually Rails application will have a helper like this:
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
You don't need this helper if you're using devise -- it's already done for you.
Getting the question id
First make sure that your associated resources are nested. So in our routes.rb file:
resources :questions do
resources :answers
end
This means that to create an answer we'll need to POST /questions/:id/answers.
The form changes to:
<%= form_for [#daily, #answer] do |answer| %>
<%= answer.label :answer, "What do you think?" %>
<%= answer.text_area :answer %>
<%= answer.button %>
<% end %>
and in the controller:
def create
#answer = Answer.new(answer_params)
#answer.user = current_user
#answer.question = Question.find(params[:id])
if #answer.save
redirect_to root
else
render 'new'
end
end

Why is this form redirecting me before giving me a chance to enter data?

Rails controller:
class VenuesController < ApplicationController
def new
#venue = Venue.new
end
def create
#venue = Venue.new(params[:venue])
if #venue.save
redirect_to root_path
end
end
def update
redirect_to search_path
end
end
Rails form:
<%= form_for(#venue) do |f| %>
<%= f.text_field :foursquare_id %>
<%= f.submit "Save" %>
<% end %>
"foursquare_id" is a column in the "venues" table. usually i import a foursquare id from foursquare but i'm typing in text for testing purposes. i am being redirected to "root_path" before even being given a chance to type into the form.
what is my controller/form missing? thank you in advance
The template form should be used by the new action with new.html.erb as the filename. And you should be going to /venues/new to fill out the form.
The create action is used to submit the completed form, which is why you are getting redirected. You should also modify create to handle a model that couldn't save:
def create
#venue = Venue.new(params[:venue])
if #venue.save
redirect_to root_path
else
render :action => :new
end
end
Or you can use the shorthand for this:
def create
#venue = Venue.new(params[:venue])
#venue.save
respond_with #venue, :location => root_path
end
Your form for creating a new venue should be in venues/new.html.erb which will call the Create action in your controller upon form submit. You shouldn't have a create view in this scenario.

Comments that belong_to Post and belong_to User

I'm trying to add comments to a Post model
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user #should this be has_one :user instead?
....
How do I set up my Comment new and creation actions to get both current_user as well as the current post?
guides.rubyonrails.org suggested
Controller:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment])
redirect_to post_path(#post)
end
View
<%= form_for([#post, #post.comments.build]) do |f| %>
...
However this only seems to be aiming to associate with the post and not also the user. How can I set up both associations?
I assume you have a current_user() method somewhere in your controller.
So this should do it:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(params[:comment])
#comment.user = current_user
#comment.save
redirect_to post_path(#post)
end
Deradon answered the question well but I prefer to have that logic within the new comment form itself. For example, instead of calling those variables you can have:
app/views/comments/_form.html.erb:
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.hidden_field :post_id, value: #post.id %>
This assumes of course that your new comment form is embedded within the 'post show' page, so that #post is available:
app/views/posts/show.html.erb:
<body>
<%= render #post %>
<%= render 'comments/_form' %>
</body>
This will add post_id and user_id directly into the db for a new comment. Also, don't forget to make an index for those foreign keys so the database has quicker access. If you don't know how, google it!

Resources