Reputation: 211
I'm trying to build a small expense tracking app using Rails 4.1. Using devise for authorization. Expense and it's nested attribute, comments belong to a user. The associations are set up in the model and expenses are getting associated with the user. Here's the Expense controller:
class ExpensesController < ApplicationController
def new
@expense = Expense.new
@item = @expense.items.build
#@comment = @expense.comments.build
end
def index
@expenses = Expense.all
#@items = Item.where(:expense_id => @expense.id)
end
def show
@expense = Expense.find(params[:id])
@items = Item.where(:expense_id => @expense.id)
end
def create
@expense = current_user.expenses.new(expense_params)
respond_to do |format|
if @expense.save
ExpenseMailer.expense_submission(@expense).deliver
format.html { redirect_to @expense, notice: 'Expense Report Submitted.' }
format.json { render :show, status: :created, location: @expense }
else
format.html { render :new }
format.json { render json: @expense.errors, status: :unprocessable_entity }
end
end
end
def edit
@expense = Expense.find(params[:id])
end
def update
@expense = Expense.find(params[:id])
#@comment = @expense.comments.build
if @expense.update(expense_params)
#if @comment.save
#ExpenseMailer.comments_added(@expense).deliver
flash[:notice] = "Expense Report Updated"
redirect_to expenses_path
#else
# flash[:notice] = "Expense Report Updated"
#redirect_to expenses_path
##end
else
render 'edit'
end
end
The form from where the comment attributes are built looks like:
<%= nested_form_for (@expense) do |f| %>
<div class="form-group">
<%= f.label :state %><br />
<%= f.select :state, Expense.states, :include_blank => false, class: "form-control" %>
</div>
<%= f.fields_for :comments, @expense.comments.build do |comment| %>
<div class="form-group">
<%= comment.label :comment%>
<%= comment.text_area :comment, class: "form-control" %>
</div>
<%= comment.hidden_field :commenter %>
<% end %>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</div>
The @comment.commenter = current_user isn't adding the current user id to the database. Should I include it in the expense controller somewhere?
Upvotes: 1
Views: 280
Reputation: 76774
Model
I just tried to create better code for your strong params
, but I couldn't work out how to include the param in your nested attributes
I would therefore recommend using the inverse_of:
method in your Comment
model to get it sorted properly:
#app/models/expense.rb
Class Expense < ActiveRecord::Base
belongs_to :user
has_many :comments, inverse_of: :expense
accepts_nested_attributes_for :comments
end
#app/models/comment.rb
Class Comment < ActiveRecord::Base
belongs_to :expense, inverse_of: :comments
before_create :populate_expense, on: :create
private
def populate_expense
self.commenter_id = self.expense.user_id
end
end
This should work if you're populating the comments from the accepts_nested_attributes_for
directive
Comments
I don't understand why you've created two create
actions for both your expenses
and comments
controllers - the controller action is meant to be independent of the Model
What I'm trying to say is that if you think the comments#create
controller action will be invoked by your nested
attribute creation, you'd be mistaken - it is only invoked when you send a request to it through the Rails router :)
If you're creating Comments
and Expenses
separately, you'll be able to use these two different actions; but they won't be invoked by each other. Only Model methods can be invoked by the controller (you shouldn't be calling other controller methods)
If you wanted to create a Comment
from the expenses#show
page, here's how you'd set it up:
#config/routes.rb
resources :expenses do
resources :comments #-> domain.com/expenses/:expense_id/comments/new
end
#app/controllers/expenses_controller.rb
Class CommentsController < ApplicationController
def new
@expense = Expense.find params[:expense_id]
@comment = @expense.comments.new
end
def create
@expense = Expense.find params[:expense_id]
@comment = @expense.comments.new(comment_params)
@comment.save
end
private
def comment_params
params.require(:comment).permit(:comment, :params).merge(commenter_id: current_user.id)
end
end
This will work if you wanted to create a comment from the expenses#show
page. If you do this, you need to ensure you are calling the comments#new
/ comments#create
actions, rather than those of the expenses
controller
Upvotes: 0
Reputation: 43298
You have to add:
@comment.commenter = current_user
below that if statement. Like this:
def create
@article = Expense.find(params[:expense_id])
if @comment = @expense.comments.create(comment_params)
@comment.commenter = current_user
@comment.save
ExpenseMailer.comments_added(@expense).deliver
redirect_to expenses_path
end
end
And then save the comment again. In your current code you're overwriting the @comment
object with the newly created object by doing:
@comment = @expense.comments.create(comment_params)
but you haven't set the commenter
on that new object anywhere yet.
Upvotes: 2