Nested fields made with cocoon are not saving - ruby-on-rails

I have 3 objects, Users, Recipes, and Tasks. Tasks are nested inside Recipes, and Recipes are nested inside Users. I am able to save/add/delete Recipes just fine, and I can add Tasks in the HTML form, but when I go to save, the Tasks do not show up as part of a Recipe, even when I go back to the form. I have been working on this for a while and would appreciate any insight.
Users Controller:
class UsersController < ApplicationController
before_filter :authenticate_user_or_admin_or_conduit!
before_action :set_user, only: [:show, :edit, :update, :destroy]
respond_to :html
def index
#users = User.all
respond_with(#users)
end
def show
respond_with(#user)
end
def new
#user = User.new
respond_with(#user)
end
def edit
end
def create
#user = User.new(user_params)
if #user.save
if conduit_signed_in?
redirect_to '/conduits', notice: 'User created successfully.'
elsif admin_signed_in?
redirect_to '/admins', notice: 'User created successfully.'
else
redirect_to #user, notice: 'User created successfully.'
end
else
render :new
end
end
def update
#user.update(user_params)
respond_with(#user)
end
def destroy
#user.destroy
respond_with(#user)
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
end
Recipes Controller:
class RecipesController < ApplicationController
before_action :set_recipe, only: [:show, :edit, :update, :destroy]
# GET /recipes
# GET /recipes.json
def index
#recipes = Recipe.all
end
# GET /recipes/1
# GET /recipes/1.json
def show
end
# GET /recipes/new
def new
#recipe = Recipe.new
end
# GET /recipes/1/edit
def edit
end
# POST /recipes
# POST /recipes.json
def create
#recipe = Recipe.new(recipe_params)
respond_to do |format|
if #recipe.save
format.html { redirect_to #recipe, notice: 'Recipe was successfully created.' }
format.json { render :show, status: :created, location: #recipe }
else
format.html { render :new }
format.json { render json: #recipe.errors, status: :unprocessable_entity }
end
end
class UsersController < ApplicationController
before_filter :authenticate_user_or_admin_or_conduit!
before_action :set_user, only: [:show, :edit, :update, :destroy]
respond_to :html
def index
#users = User.all
respond_with(#users)
end
def show
respond_with(#user)
end
def new
#user = User.new
respond_with(#user)
end
def edit
end
def create
#user = User.new(user_params)
if #user.save
if conduit_signed_in?
redirect_to '/conduits', notice: 'User created successfully.'
elsif admin_signed_in?
redirect_to '/admins', notice: 'User created successfully.'
else
redirect_to #user, notice: 'User created successfully.'
end
else
render :new
end
end
def update
#user.update(user_params)
respond_with(#user)
end
def destroy
#user.destroy
respond_with(#user)
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
end
Recipes Controller:
class RecipesController < ApplicationController
before_action :set_recipe, only: [:show, :edit, :update, :destroy]
# GET /recipes
# GET /recipes.json
def index
#recipes = Recipe.all
end
# GET /recipes/1
# GET /recipes/1.json
def show
end
# GET /recipes/new
def new
#recipe = Recipe.new
end
# GET /recipes/1/edit
def edit
end
# POST /recipes
# POST /recipes.json
def create
#recipe = Recipe.new(recipe_params)
end
# PATCH/PUT /recipes/1
# PATCH/PUT /recipes/1.json
def update
respond_to do |format|
if #recipe.update(recipe_params)
format.html { redirect_to #recipe, notice: 'Recipe was successfully updated.' }
format.json { render :show, status: :ok, location: #recipe }
else
format.html { render :edit }
format.json { render json: #recipe.errors, status: :unprocessable_entity }
end
end
end
# DELETE /recipes/1
# DELETE /recipes/1.json
def destroy
#recipe.destroy
respond_to do |format|
format.html { redirect_to recipes_url, notice: 'Recipe was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_recipe
#recipe = Recipe.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def recipe_params
params.require(:recipe).permit(:reward, task_attributes: [:description, :counter, :done, :_destroy, :id])
end
end
Tasks Controller:
class TasksController < ApplicationController
before_action :set_task, only: [:show, :edit, :update, :destroy]
# GET /tasks
# GET /tasks.json
def index
#tasks = Task.all
end
# GET /tasks/1
# GET /tasks/1.json
def show
end
# GET /tasks/new
def new
#task = Task.new
end
# GET /tasks/1/edit
def edit
end
# POST /tasks
# POST /tasks.json
def create
#task = Task.new(task_params)
respond_to do |format|
if #task.save
format.html { redirect_to #task, notice: 'Task was successfully created.' }
format.json { render :show, status: :created, location: #task }
else
format.html { render :new }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /tasks/1
# PATCH/PUT /tasks/1.json
def update
respond_to do |format|
if #task.update(task_params)
format.html { redirect_to #task, notice: 'Task was successfully updated.' }
format.json { render :show, status: :ok, location: #task }
else
format.html { render :edit }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
# DELETE /tasks/1
# DELETE /tasks/1.json
def destroy
#task.destroy
respond_to do |format|
format.html { redirect_to tasks_url, notice: 'Task was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_task
#task = Task.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def task_params
params.require(:task).permit(:description, :counter, :done, :notes)
end
end
Users Model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :characters
end
Recipes Model:
class Recipe < ActiveRecord::Base
belongs_to :character
has_many :task, :dependent => :destroy
accepts_nested_attributes_for :task, allow_destroy: true
end
Tasks Model:
class Task < ActiveRecord::Base
belongs_to :recipe
end
User Form:
<div id="myform">
<%= form_for(#character) do |f| %>
<% if #character.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#character.errors.count, "error") %> prohibited this character from being saved:</h2>
<ul>
<% #character.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<li class="accordion-navigation">
Recipes
<div id="panel6a" class="content">
<fieldset>
<legend>Recipes consist of a variety of tasks</legend>
<div>
<%= f.fields_for :recipe do |recipe| %>
<%= render "recipe_fields", :f => recipe %>
<% end %>
<div class="links">
<%= link_to_add_association "Add Recipe", f, :recipe, :class =>"button" %>
</div>
</div>
</fieldset>
</div>
</li>
<% end %>
</div>
Recipe Form:
<div class="nested-fields">
<div class="row">
<div class="row">
<div class="large-3 columns">
<div class="field">
<%= f.text_field :reward %>
</div>
</div>
<div class="large-9 columns">
<fieldset>
<legend>Add Task</legend>
<div>
<%= f.fields_for :task do |task| %>
<%= render "task_fields", :f => task %>
<% end %>
<div class="links">
<%= link_to_add_association "Add Task", f, :task, :class =>"small button" %>
</div>
</div>
</fieldset>
<span style="float:right"><%= link_to_remove_association "Remove Recipe", f, data: {confirm: "Are you sure?"}, :class =>"button alert" %></span>
</div>
</div>
</div>
</div>
Task Form:
<div class="nested-fields">
<div class="row">
<div class="large-3 columns">
<div class="field">
<%= f.text_field :description %>
</div>
</div>
<div class="large-3 columns">
<div class="field">
<%= f.number_field :counter, label: "Record number if needed" %>
</div>
</div>
<div class="large-3 columns">
<div class="field">
<%= f.check_box :done, label: "Completed?" %>
</div>
</div>
<span style="float:right"><%= link_to_remove_association "Remove Task", f, data: {confirm: "Are you sure?"}, :class =>"small button alert" %></span>
</div>
</div>

I dealt with this funkiness recently. I'm guessing the Task is being created, just not relating to the Recipe. If that is the case, you need to include the recipe_id in the task_attributes array.
Nesting is all sorts of magic, but for whatever reason, the id of the parent object won't be set unless it's explicitly included as a permitted param.
Hope this helps!

The other post definitely pointed out an error, but I could not fix it until I added the task_attributes to the recipe_attributes in the User controller, like so:
params.require(:user).permit(:name, recipe_attributes: [:reward, :_destroy, :user_id, :id, task_attributes: [:description, :recipe_id, :counter, :done, :_destroy, :id]])

Related

Images are uploading to S3 but I cannot see them in my app

I am still very new to rails is this is the first time I am uploading files to S3. In my app I have used the carrierwave gem and am able to choose a file and save. It seems like the link between carrierwave and S3 are working because the images are showing up in my S3 bucket. The problem is I can not see the image in my app and I cannot figure out why this is.
Here is my controller:
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
# GET /products
# GET /products.json
def index
#products = Product.all
#uploader = Product.new.image
#uploader.success_action_redirect = new_product_path
end
# GET /products/1
# GET /products/1.json
def show
end
# GET /products/new
def new
#product = Product.new(key: params[:key])
end
# GET /products/1/edit
def edit
end
# POST /products
# POST /products.json
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /products/1
# PATCH/PUT /products/1.json
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to #product, notice: 'Product was successfully updated.' }
format.json { render :show, status: :ok, location: #product }
else
format.html { render :edit }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# DELETE /products/1
# DELETE /products/1.json
def destroy
#product.destroy
respond_to do |format|
format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product
#product = Product.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def product_params
params.require(:product).permit(:title, :description, :image, :price, :category, :subcategory)
end
end
ImageUploder:
# encoding: utf-8
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWaveDirect::Uploader
include CarrierWave::RMagick
include CarrierWave::MimeTypes
process :set_content_type
version :thumb do
process resize_to_fill: [200,200]
end
end
show.html.erb (where the image should be showing up):
<p id="notice"><%= notice %></p>
<p>
<strong>Title:</strong>
<%= #product.title %>
</p>
<p>
<strong>Description:</strong>
<%= #product.description %>
</p>
<p>
<strong>Image:</strong>
<%= #product.image_url %>
</p>
<p>
<strong>Price:</strong>
<%= #product.price %>
</p>
<p>
<strong>Category:</strong>
<%= #product.category %>
</p>
<p>
<strong>Subcategory:</strong>
<%= #product.subcategory %>
</p>
<%= link_to 'Edit', edit_product_path(#product) %> |
<%= link_to 'Back', products_path %>
product.rb:
class Product < ActiveRecord::Base
attr_accessor :image
mount_uploader :image, ImageUploader
after_save :enqueue_image
def image_name
File.basename(image.path || image.filename) if image
end
def enqueue_image
ImageWorker.perform_async(id,key) if key.presemt?
end
class ImageWorker
include Sidekiq::Worker
def perform(id, key)
product = Product.find(id)
product.key = key
product.remote_image_url = product.image.direct_fog_url(with_path: true)
end
end
end
Thanks in advance for any help!
Try changing
<%= #product.image_url %>
to
<%= image_tag(#product.image_url) %>

Nested resources in rails not saving

Ive setup some nested resources in Rails using the following:
resources :notes do
resources :comments
end
But even though /notes/1/comments/new will take me to a new comments page, it does not make the association of the note for the comment. I have included an note_id field in the comment but this does not get populate. Any tips?
Comments controller:
class CommentsController < ApplicationController
before_action :set_comment, only: [:show, :edit, :update, :destroy]
# GET /comments
# GET /comments.json
def index
#comments = Comment.all
end
# GET /comments/1
# GET /comments/1.json
def show
end
# GET /comments/new
def new
#comment = Comment.new
end
# GET /comments/1/edit
def edit
end
# POST /comments
# POST /comments.json
def create
#comment = Comment.new(comment_params)
respond_to do |format|
if #comment.save
format.html { redirect_to #comment, notice: 'Comment was successfully created.' }
format.json { render :show, status: :created, location: #comment }
else
format.html { render :new }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /comments/1
# PATCH/PUT /comments/1.json
def update
respond_to do |format|
if #comment.update(comment_params)
format.html { redirect_to #comment, notice: 'Comment was successfully updated.' }
format.json { render :show, status: :ok, location: #comment }
else
format.html { render :edit }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
# DELETE /comments/1
# DELETE /comments/1.json
def destroy
#comment.destroy
respond_to do |format|
format.html { redirect_to comments_url, notice: 'Comment was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_comment
#comment = Comment.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def comment_params
params.require(:comment).permit(:note_id, :comment)
end
end
Form code:
<%= form_for(#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 |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :note_id %><br>
<%= f.text_field :note_id %>
</div>
<div class="field">
<%= f.label :comment %><br>
<%= f.text_area :comment %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You should create your Comment like this in your controller:
class CommentsController < ApplicationController
before_action :set_comment, :set_note, only: [:show, :edit, :update, :destroy]
# Build your comment from your #note
# this sets all the association values correct (like ids)
def create
#comment = #note.comments.build(comment_params)
# etc...
end
def set_note
#note = Note.find(params[:note_id])
end
#...
end
So, using the #note.commments.build will help you on your issue. But of course, you need to query the #note using the note_id in the params hash.

HABTM Association Build

I need propagate this values in :departaments_products table: , but I received the error:
I'm using Rails 4
NoMethodError in Products#new
undefined method `departament_id' for #<Product:0x007f916d35d648>
view.html.erb:
<%= form_for(#product) do |f| %>
<% if #product.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#product.errors.count, "error") %> prohibited this product from being saved:</h2>
<ul>
<% #product.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :price %><br>
<%= f.text_field :price %>
</div>
<%= f.collection_select(:departament_id, Departament.all, :id, :name, {:include_blank => true}) %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Products_controller:
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
# GET /products
# GET /products.json
def index
#products = Product.all
end
# GET /products/1
# GET /products/1.json
def show
#product = Product.find( params[:id] )
end
# GET /products/new
def new
#product = Product.new
end
# GET /products/1/edit
def edit
end
# POST /products
# POST /products.json
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Produto criado com sucesso' }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /products/1
# PATCH/PUT /products/1.json
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to #product, notice: 'Product was successfully updated.' }
format.json { render :show, status: :ok, location: #product }
else
format.html { render :edit }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# DELETE /products/1
# DELETE /products/1.json
def destroy
#product.destroy
respond_to do |format|
format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product
#product = Product.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def product_params
params.require(:product).permit(:name, :price)
end
end
Models:
class Departament < ActiveRecord::Base
has_and_belongs_to_many :products
end
class Product < ActiveRecord::Base
has_and_belongs_to_many :departaments
end
Migration:
class AddProductsAndDepartaments < ActiveRecord::Migration
def change
create_table :departaments_products do |t|
t.references :product, :departament
end
end
end
As its a HABTM association, logically you should be selecting multiple departament_ids for a single product. That said, you should include multiple: true option in the collection_select for departament_ids (Notice departament_ids in plural) in your view code:
<%= f.collection_select(:departament_ids, Departament.all, :id, :name, {include_blank: true}, {multiple: true}) %>
Currently, you are accessing it as departament_id (Notice singular) BUT as per HABTM association you get a method named departament_ids (Notice plural) and NOT departament_id which is why you receive error as NoMethodError in Products#new undefined method 'departament_id'
Once you are done with this change, you need to permit the departament_ids field in ProductsController as below:
def product_params
params.require(:product).permit(:name, :price, :departament_ids => [])
end
:departament_ids => [] is used because multiple selection is allowed for departament_ids and so you would receive it as an Array in params hash upon form submission.
Try departament_ids
For has_many => departament_ids
For has_one => departament_id

Ruby on Rails form_for reference class id and pass to child comment

I have a class called "questions", which is similar to an article, and each of them can have comments. Now the problem is, that I want to show multiple questions on the index page and all displaying the comments of the specific question as well as a small little form to leave a comment, which should be added to its question. Basically I have added the form and done everything, apart from figuring out how to get the question id and pass it to the comment.
I have made a little screenshot as well: http://prntscr.com/2pjk0i
questions_controller.rb
class QuestionsController < ApplicationController
before_action :set_question, only: [:show, :edit, :update, :destroy]
# GET /questions
# GET /questions.json
def index
#current_user ||= User.find_by_id(session[:user_id])
#questions = Question.all
end
# GET /questions/1
# GET /questions/1.json
def show
end
# GET /questions/new
def new
#question = Question.new
end
# GET /questions/1/edit
def edit
end
# POST /questions
# POST /questions.json
def create
#question = Question.new(question_params)
#current_user ||= User.find_by_id(session[:user_id])
#question.update(:user_id => #current_user.id)
respond_to do |format|
if #question.save
format.html { redirect_to #question, notice: 'Question was successfully created.' }
format.json { render action: 'show', status: :created, location: #question }
else
format.html { render action: 'new' }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /questions/1
# PATCH/PUT /questions/1.json
def update
respond_to do |format|
if #question.update(question_params)
format.html { redirect_to #question, notice: 'Question was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# DELETE /questions/1
# DELETE /questions/1.json
def destroy
#question.destroy
respond_to do |format|
format.html { redirect_to questions_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_question
#question = Question.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def question_params
params.require(:question).permit(:title, :body)
end
end
comments_controller.rb
class CommentsController < ApplicationController
before_action :set_comment, only: [:show, :edit, :update, :destroy]
# GET /comments
# GET /comments.json
def index
#current_user ||= User.find_by_id(session[:user_id])
#comments = Comment.all
end
# GET /comments/1
# GET /comments/1.json
def show
end
# GET /comments/new
def new
#comment = Comment.new
end
# GET /comments/1/edit
def edit
end
# POST /comments
# POST /comments.json
def create
#comment = Comment.new(comment_params)
#current_user ||= User.find_by_id(session[:user_id])
#comment.update(:user_id => #current_user.id, :question_id => ?) # What to add here to get the specific question id?
respond_to do |format|
if #comment.save
format.html { redirect_to '/', notice: 'comment was successfully created.' }
format.json { render action: 'show', status: :created, location: #comment }
else
format.html { render action: 'new' }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /comments/1
# PATCH/PUT /comments/1.json
def update
respond_to do |format|
if #comment.update(comment_params)
format.html { redirect_to '', notice: 'comment was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
# DELETE /comments/1
# DELETE /comments/1.json
def destroy
#comment.destroy
respond_to do |format|
format.html { redirect_to '' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_comment
#comment = Comment.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def comment_params
params.require(:comment).permit(:title, :body)
end
end
index.html.erb
<h1>Listing questions</h1>
<%= link_to 'New Question', new_question_path %>
<hr>
<% #questions.each do |question| %>
<!-- Author -->
<%= question.user.name %> <br>
<!-- Date -->
<%= question.created_at %> <br>
<!-- Title -->
<%= question.title %> <br>
<!-- Body -->
<%= question.body %> <br>
<%= question.id %> <br>
<!-- Comment count -->
<%= question.comments.size %> Comment <br>
<!-- Comments -->
<% question.comments.each do |comment| %>
<!-- Comment Author -->
<%= comment.user.name %> <br>
<!-- Comment Date -->
<%= comment.created_at %> <br>
<!-- Comment Body -->
<%= comment.body %> <br>
<% end %>
<%= form_for(question.comments.new) do |f| %>
<div class="field">
<%= f.label :body %><br>
<%= f.text_area :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<hr>
<% end %>
Thank you in advance for your help! :)
The form_for will need to submit the question_id somehow - either by a route or through the form. I recommend a route.
If you don't interact with comments independently - if there is always a question, then change your routes to something like this:
resources :questions do
resources :comments
end
Then - in your form for, you will do this
<%= form_for [question, question.comments.new] do |f| %>
This will cause the form to submit (POST) to /question/:question_id/comments and you can handle it from there.
In the comments controller - you'll get the question from the params[:question_id] and return the result via an ajax response (respond to json).
This part is still tricky if you haven't done it before. If you need help with that part, you can probably find good examples or ask a separate question...
you can add hidden field inside your form
<%= f.hidden_field :question_id, value: question.id %>
or you can change your form
<%= form_for :comment, :url => comments_path(question_id:question.id) do |f| %>
<div class="field">
<%= f.label :body %><br>
<%= f.text_area :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
when u submit this form u will have url like /comments?question_id=id

How to allow users to comment

So, I'd like users to be able to comment. At the present moment anyone can comment just by typing in a arbitrary name in the name field.
But I'd like to associate a comment with a user. So there will no longer be a need for a name field in the comments form as it will be the users name.
How can this be done?
I've followed Ryan Bates railscast but he never associates comments with users.
comments_controller.rb
class CommentsController < ApplicationController
before_action :set_comment, only: [:show, :edit, :update, :destroy]
def index
#comments = Comment.where("song_id IS NOT ?", nil)
end
def show
end
# GET /comments/new
def new
end
# GET /comments/1/edit
def edit
end
# POST /comments
# POST /comments.json
def create
#comment = Comment.new(comment_params)
respond_to do |format|
if #comment.save
format.html { redirect_to song_url(#comment.song_id), notice: 'Comment was successfully created.' }
format.json { render action: 'show', status: :created, location: #comment}
else
format.html { render action: 'new' }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /comments/1
# PATCH/PUT /comments/1.json
def update
respond_to do |format|
if #comment.update(comment_params)
format.html { redirect_to song_url(#comment.song_id), notice: 'Comment was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
# DELETE /comments/1
# DELETE /comments/1.json
def destroy
#comment.destroy
redirect_to song_url(#comment.song_id)
end
private
# Use callbacks to share common setup or constraints between actions.
def set_comment
#comment = Comment.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def comment_params
params.require(:comment).permit(:song_id, :author_name, :site_url, :content, :user_id)
end
end
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :songs
has_many :comments
acts_as_voter
end
comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :song
end
comments#form.html.erb
<%= form_for #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 %>
<div id="comment_form">
<div class="field">
<%= f.hidden_field :song_id %>
<p>
<%= f.text_field :author_name, placeholder: "Name" %>
</p>
<p>
<%= f.text_area :content, :rows => '12', :cols => 35, placeholder: "Leave a comment..." %>
</p>
<p><%= f.submit "Submit" %></p>
<% end %>
<br /><br />
</div></div>
Your Comment table should have a column named user_id if it does not already. Then you can assign the user_id two different ways. These assume you have a current_user method. If you do not, then you will have to fill in the user_id from whatever session store or method you are using.
You could create a hidden_field in your form to assign it.
<%= f.hidden_field :user_id, value: current_user.id %>
but as noted by #rmagnum2002 this could be a security concern due to a user could edit this.
You could assign it during the create action:
def create
#comment = Comment.new(comment_params)
#comment.user_id = current_user.id
respond_to do |format|
if #comment.save
format.html { redirect_to song_url(#comment.song_id), notice: 'Comment was successfully created.' }
format.json { render action: 'show', status: :created, location: #comment}
else
format.html { render action: 'new' }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
Assigning this in the controller create action is probably best.

Resources