I have a form that is saving all but one attribute. :user_id (a foreign key) persists in coming out as nil. This is a form for creating a new comment, and it's located in my impressions#show file, so that may be part of the problem; but the other attributes (:name and :impression_id) seem to be saving fine.
Here's the form:
= form_for #comment do |f|
= f.label 'Add Comment'
%br
= f.text_area :body, placeholder: 'Comment', cols: 50, rows: 3
%br
= f.hidden_field :impression_id, :value => #impression.id
= f.hidden_field :user_id, :value => current_user.id
%br
= f.submit "Save"
The relevant section of the impressions_controller is:
def show
#impression = Impression.find(params[:id])
#play = Play.find_by(id: #impression.production.play_id)
#production = Production.find_by(id: #impression.production_id)
#comments = Comment.where(impression_id: #impression.id)
#comment = Comment.new
end
In the comments_controller, I have the following:
def new
#impression = Impression.find(params[:impression_id])
#comment = Comment.new(comment_params)
authorize #comment
end
def create
#comment = Comment.new(comment_params)
authorize #comment
if #comment.save
flash[:notice] = 'You have successfully added a comment.'
redirect_to :back
else
flash[:notice] = 'Please try again.'
render :new
end
end
private
def comment_params
params.require(:comment).permit(:body, :impression_id, :user_id)
end
As you can see, I'm trying to assign :user_id the value of current_user.id, but it isn't taking. Can anyone see what I'm missing?
I've tried adding comment_params to my impressions controller, but I get an error message that the param comment is empty . . .
Thanks!
First check current_user if the value of current_user is coming fine then it has to save user_id in your comments table.
If your user is not logged in, current_user is nil.
Make sure that your user is logged in
To answer your question...
Maybe your hidden field is not sending user_id inside the comments subhash. Check your params and see. Your comment_params method expects it to be inside the comments subhash.
And how do you check these params?
Either look at your rails console after each form submission, or
if you are using rails 4.2, check https://gorails.com/episodes/rails-4-2-introduction about 4 minutes in...,
or...
in your gemfile, put
group :development do
gem 'better_errors'
gem 'binding_of_caller'
end
bundle (in your terminal), restart your rails server
and then in your controller code, at a line where current_user SHOULD exist, type the line raise "oops", run the code, get an error, then go to http://localhost:3000/__better_errors to see your console
This will give you an error page with a console, in which you can type out any variable name (params, current_user, whatever) and check its value.
Related
I'm having what I assume must be a simple problem but I just can't figure it out. I'm trying to update an attribute in one model when another is created.
In my view:
<%= link_to 'Click here to rate this user', new_user_review_path(:user_id => request.user.id, :gigid => request.gig.id), remote: true %>
Which passes params :gigid and :user_id
Than my controller:
def new
#review = Review.new
#gig = Gig.find(params[:gigid])
end
def create
#review = #user.reviews.new review_params
#review.reviewed_id = current_user.id
if #review.save
#gig.update(reviewed: true)
respond_to do |format|
format.html {redirect_to session.delete(:return_to), flash[:notice] = "Thankyou for your rating!"}
format.js
end
else
render 'new'
end
end
But I get undefined method 'update'for nil:NilCLass:
I know the params are passing and the 'Gig' can be updated as :
def new
#review = Review.new
Gig.find(params[:gigid]).update(reviewed: true)
end
updates the attribute fine, but when I click 'New review' not when the review is actually created.
Adding :
def create
#review = #user.reviews.new review_params
#review.reviewed_id = current_user.id
if #review.save
Gig.find(params[:gigid]).update(reviewed: true)
etc etc etc
gives me the same undefined method 'update'for nil:NilCLass:
I have tried with find_by_id instead of find which makes no difference.
EDIT:
def create
#gig = Gig.find params[:gigid]
#review = #user.reviews.new review_params
#review.reviewed_id = current_user.id
if #review.save
#gig.update(reviewed: true)
etc etc etc
Doesn't work either. I get no errors, but the gig ID is still 'nil'.
The params are passing to the 'New' action but not the 'Create' action. I feel this should be very easy but I'm just not seeing it at the moment.
But I get undefined method 'update'for nil:NilCLass:
The error is that you have not defined #gig in your create action.
Since Rails is built on HTTP, and HTTP is stateless, you have to set the "instance" variables with each new request:
def new
#review = Review.new
#gig = Gig.find params[:gigid]
end
def create
#gig = Gig.find params[:gigid]
#review = #user.reviews.new review_params
A much better pattern for you would be to use the after_create callback in your Review model:
#app/models/review.rb
class Review < ActiveRecord::Base
belongs_to :gig #-> I presume
after_create :set_gig
private
def set_gig
self.gig.update(reviewed: true)
end
end
--
If you wanted to make the Gig update within your current setup, you'll be best sending the gig_id param through the request (not the link):
#app/views/reviews/new.html.erb
<%= form_for [#user, #review] do |f| %>
<%= f.hidden_field :gig_id, #gig.id %> #-> params[:reviews][:gig_id]
...
<% end %>
This will make params[:review][:gig_id] available in the create action, with which you'll be able to use in your code.
The problem is, you never assigned a value to #gig in your create method. I can't see your form, but you need something like this in your create method:
#gig = Gig.find params[:gigid]
Assuming that you're passing the parameter :gigid to #create
In the second example you showed, I'm not sure what's going on, but you should be getting a ActiveRecord::RecordNotFound exception on the find().
Try the below code for update operation.
gig_record = Gig.find_by_id(params[:gigid])
gig_record.update_attribute(reviewed: true) unless gig_record.blank?
Trying to update 2 attributes to a User model, this is my current code in the Users controller:
def update
#user = User.find(params[:id])
if #user.update_attributes(songkickID: params[:user][:songkickID], jamID: params[:user][:jamID])
redirect_to #user
else
redirect_to #user
end
end
The Songkick ID and the Jam ID are entered into 2 different fields. However, with the current code, if I attempt to update the Jam ID on its own, it updates that attribute, but then redirects to the user page (as expected), where the Songkick ID is now nil. Upon entering the Songkick ID again, the Jam ID becomes nil. I suppose this is because they are both part of the same if statement in the controller?
I attempted to use an elsif for the jamID params, but it does not seem to recognise at all (i.e. won't update that attribute for the user). Also attempted || conditional operator.
EDIT: Here's the 2 different forms:
<%= form_for(#user) do |f| %>
<%= f.text_field :jamID, :id=>"jamURL" %>
<%= f.submit "Jam ID", :onclick => "changeImg()", id: "saveJam" %>
<% end %>
and
<%= form_for(#user) do |f| %>
<%= f.text_field :songkickID %>
<%= f.submit "Songkick ID", :type => :image, :src => image_path("songkicklogo.png"), id: "skLogo" %>
<% end %>
And I tried modifiying the code to update_column, but I get wrong number of arguments (1 for 2).
EDIT 2: Following layout from Hartl's Rails Tutorial, I attempted this to define strong parameters:
private
def user_params
params.require(:user).permit(:songkickID, :jamID)
end
But I still get the Forbidden Attributes Error?
EDIT 3: The following code passes, but I worry it doesn't adhere to Rails 4 strong parameters:
Controller:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def edit
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:songkickID, :jamID)
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
redirect_to #user
else
redirect_to #user
end
end
end
If I move update to below the update method, I get an undefined variable/method error for user_params, and I cannot make it private.
So - why are you explicitly naming the attributes in your update_attributes?
You should be able to use the following:
#user.update_attributes(params[:user])
Remember that if you've named your form fields correctly, params[:user] is a hash that will already have the keys you want (:songkickID etc)
Now - you will get one of two things coming through to your action, which you then pass through to update_attributes as:
{:songkickID => someID}
{:jamID => someOtherID}
which will correctly update your user and only change the one that is passed.
The problem with your earlier code was that what you passed to update attribute was:
{:songkickID => someID, :jamID => nil}
{:songkickID => nil, :jamID => someOtherID}
which was deliberately overwriting the other id with the nil you passed.
EDIT from OP: Thanks for this, and here's my final controller code:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
redirect_to #user
else
redirect_to #user
end
end
private
def user_params
params.require(:user).permit(:songkickID, :jamID)
end
end
In last case scenario:
def update
if params[:user][:songkickID]
received_param = { songkickID: params[:user][:songkickID] }
elsif params[:user][:jamID]
received_param = { jamID: params[:user][:jamID] }
end
#user.update_attributes(received_param)
redirect_to #user
end
Note: I removed the last condition since it wasn't useful
I have a #miniatures model and a #lines model joined via a #minilines model.
In the #miniature show view I have this link
<%= link_to "Add to product line", new_miniline_path(:miniature_id => #miniature) %>
To a New #miniline form that takes the :miniature_id from a hidden field like so
<%= f.hidden_field :miniature_id, :value => #miniature.id %>
And then you select the desired #line from a dropdown.
This all works. What I can't get to work is for the controller to redirect a user back to the originating #miniature after the create action works.
This is what I have in my new and create actions in the controller
def new
#miniline = Miniline.new(#miniature)
#miniature = Miniature.find(params[:miniature_id])
#lines = Line.all
end
def create
#miniline = Miniline.new(miniline_params)
if #miniline.save
flash[:success] = "Miniature added to product line"
redirect_to miniature_path(#miniature)
else
flash[:success] = "Did not work!!!"
render 'new'
end
end
I've tried various alternatives to miniature_path(#miniature) like plain #miniature and miniature_path(:miniature_id) but to no avail. I suspect my problem is with the passing of the :miniature_id to the #minilines model. Any help very much appreciated as I've been banging my head for an hour or two.
redirect_to miniature_path(#miniline.miniature)
Assuming you have an association setup.
You haven't set #miniature in create which is that that doesn't work
You could also do
redirect_to #miniline.miniature
Currently i'm using form_for read read a text_field called :comment, after the user submit it goes to the controller as such:
def create
#entry = Entry.new(entry_params)
if #entry.save
redirect_to :back
else
render 'new'
end
end
private
def entry_params
params.require(:entry).permit(:comment)
end
my question is whether I can search inside :comment (string) for a substring "test", when submitting?
Thanks
You can access what was submitted in the comment text field with params[:entry][:comment], so you should be able to compare it to the value you want with params[:entry][:comment].include?("test")
I have a Post that has_many :comments and a Comment that belongs_to :post.
On /posts/:id (the post show method) I render a form where users can leave comments.
It all works, validations, tests and posting is just fine. Only thing missing is how to re-render the POSTed data on validation errors.
The (simplified) code for this is:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
#posts = Post.all_published(params[:page])
#title = "Blog"
end
def show
#post = Post.where({:published => true}).find(params[:id])
#comment = Comment.new(:post => #post)
#title = #post.title
end
end
#app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def create
#comment = Comment.new(params[:comment])
puts #comment
if #comment.save
flash[:notice] = 'Comment was successfully created.'
redirect_to(#comment.post)
else
flash[:notice] = "Error creating comment: #{#comment.errors}"
redirect_to(#comment.post)
end
end
end
#app/views/posts/show.haml
.html renders Post contents.
- form_for #comment do |f|
= f.hidden_field :post_id
= f.text_area :body
= f.text_field :name
.some more fields.
I expect the solution to be either in some magical declaration in the comments_controller.rb, part
else
flash[:notice] = "Error creating comment: #{#comment.errors}"
redirect_to(#comment.post)
end
Or in the PostsController.show where I prepare the #comment. Should I set that #comment conditional and fill it with some magic variable on errors?
Or did I make some entirely different mistake?
If you redirect, that data is usually lost, thats why in most cases in create create actions you would have noticed that in the false scenario, render not redirect_to.
So instead you could just try,
flash[:notice] = ""Error creating comment: #{#comment.errors}"
render :template => "posts/show"
#post = #comment.post
# you may need to pre-populate the instance variables used inside PostsController#show