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?
Related
I would like to create an article with other user not current_user and for that I'm saving in a session the id to the other user and I recover this id with a collection in the view to this point everything work fine but when I'm trying to use my helper :selected_user into my articles controller with a if sentence doesn't work here is my code:
def new
if selected_user.present?
#article = selected_user.articles.build state: :step1
render_wizard
else
#article = current_user.articles.build state: :step1
render_wizard
end
end
so, I'm asking if the selected_user.present? I would like to create the article with this user_id but else I would like to create it with the current_user
my create method is:
def create
if selected_user.present?
step = :step1
#article = selected_user.articles.build article_params_step1
#article.state = step.to_s
if #article.save
redirect_to wizard_path(next_step, article_id: #article)
else
render_wizard
end
else
step = :step1
#article = current_user.articles.build article_params_step1
#article.state = step.to_s
if #article.save
redirect_to wizard_path(next_step, article_id: #article)
else
render_wizard
end
end
end
so, yeah when I run my view the controller jump to the else section.
just for clarify my selected_user not return nil but here is the implementation:
selections_controller.rb:
class SelectionsController < ApplicationController
before_action :authenticate_user!
def create
session[:selected_user_id] = params[:user][ :user_id]
redirect_to root_path
end
end
and in my application_controller.rb:
helper_method :selected_user
def selected_user
#selected_user ||= User.find(session[:selected_user_id])
end
and in the view:
<%= form_tag( { :controller => "selections", :action => "create" } , :class => "navbar-form navbar-left") do %>
<%= collection_select(:user, :user_id, User.all, :id, :name, prompt: "Escoge cliente")%>
<%= submit_tag 'Enviar' %>
<% end %>
if I try create an article without select an user from my collection appear this error:
Couldn't find User with 'id'=
but when I select my user from the collection everything works fine. so just I want when I don't select nothing create with the current_user.
Thanks for your time !
Regards !
The reason why you were seeing the error
Couldn't find User with 'id'=
when you haven't selected a user was that the session[:selected_user_id] was nil and your old selected_user with following code was throwing the error.
def selected_user
#selected_user ||= User.find(session[:selected_user_id])
end
User.find method expects either a single id or an array of ids. If you give a single id and if it finds the relevant record in the database then it will returns that instance. If you give an array of ids and if it finds those relevant records in the database, then it will return array of those instances. But if you pass nil to it, then it will through the error Couldn't find User with 'id'= as it won't find a relevant record.
But your updated selected_user implementation:
def selected_user
#selected_user ||= session[:selected_user_id] && User.find_by_id(session[:selected_user_id])
end
is working because, first you are checking for the existence of session[:selected_user_id] value and second you are using User.find_by_id instead of User.find.
User.find_by_id either returns a single instance of the record if it finds it in the database or will return nil if it doesn't find the record. It will never through an error.
Refer to ActiveRecord#find and ActiveRecord#find_by for more info.
I'm not sure why is working and what is the different but my solution for the problem it was to add this to my selected_user method:
def selected_user
#selected_user ||= session[:selected_user_id] && User.find_by_id(session[:selected_user_id])
end
and with that I don't have the nil error and entry to the if statement without errors.
Is it safe/acceptable to send params this way to create action in the controller? Is there any potential problems?
<%= link_to "Acceptance", acceptances_path(acceptance: {favor_id: #favor.id, user_id: current_user.id}), method: :post %>
and then in controller
class AcceptancesController < ApplicationController
def create
#acceptance = Acceptance.new(acceptance_params)
if #acceptance.save
redirect_to favors_path
else
render :template => 'favors/index'
end
end
private
def acceptance_params
params.require(:acceptance).permit(:favor_id, :user_id)
end
end
Thanks for your time in advance!
The best (and the safest) you could do is assigning these id's in controller.
Since you have access to #favor and current_user objects, you'd be better of doing this:
def create
#acceptance = Acceptance.new(acceptance_params)
#acceptance.favor_id = #favor.id
#acceptance.user_id = current_user.id
# code omitted
end
I am trying to build nested routes and as mentioned here I m trying to edit my boat picture as <%= link_to "edit", edit_boat_picture_path(#boat, picture) %>. But when I try it, it throws an error undefined local variable or methodpicture' for #<#:0x007f9637811ee0>`
my picture controller is; (probably destroy is wrong also)
class PicturesController < ApplicationController
before_action :logged_in_user
before_filter :load_parent
def index
#picture = #boat.pictures.all
end
def new
#picture = #boat.pictures.new
end
def show
#pictures = #boat.pictures.find(params[:id])
end
def create
#picture = #boat.pictures.new(picture_params)
if #picture.save
#flash[:success] = "Continue from here"
render 'show'
else
render 'new'
end
end
def edit
#picture = Picture.find(params[:id])
end
def update
#picture = #boat.pictures.find(params[:id])
if #picture.update_attributes(picture_params)
flash[:notice] = "Successfully updated picture."
render 'show'
else
render 'edit'
end
end
def destroy
#picture = #boat.pictures.find(params[:id])
#picture.destroy
flash[:notice] = "Successfully destroyed picture."
redirect_to #picture.boat
end
private
def picture_params
params.require(:picture).permit(:name, :image)
end
def load_parent
#boat = Boat.find(params[:boat_id])
end
end
Presumably you should change
<%= link_to "edit", edit_boat_picture_path(#boat, picture) %>
to
<%= link_to "edit", edit_boat_picture_path(#boat, #picture) %>
The key there being changing picture to #picture. The reason to do this is that you're declaring #picture (an instance variable) in your controller, not picture (a local variable). When declaring and defining an instance variable in a method in your controller, it's also accessible in the corresponding view. However, when declaring a local variable in a method in your controller, it is not available in your view.
So even if you had declared picture rather than #picture in your controller method, it wouldn't be accessible in your view, since it's a local variable.
For more information on the five types of ruby variables, see this link.
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
Ok, another annoying problem.
I have a GuestsController that with an index action like this:
def index
#booking = Booking.find(session[:booking_id]) #i have also hard coded values to make sure the session isn't the issue
#guest = Guest.find(session[:guest_id])
end
and a personal action (to perform updates) as follows:
def personal
#guest = Guest.find(session[:guest_id])
if #guest.update(post_params)
redirect_to :controller => 'guests', :action => 'cards'
else
render 'index'
end
end
My index.html.erb view uses the #booking variable:
<%= #booking.friendly_id %> #this is one example
and also contains the form to submit the "name" field to the personal action. It updates fine if the data is valid but the #booking variable doesn't exist if it's invalid???
I need to show validation errors so I can't just use redirect_to.
The error I get is: NoMethodError in Guests#personal and undefined method `friendly_id' for nil:NilClass
Any ideas?
Just initialize the object in else part
else
#booking = Booking.find(session[:booking_id])
render 'index'
end
How about moving #booking and #guest definitions to before_filter?
before_filter do
#booking = Booking.find(session[:booking_id]) #i have also hard coded values to make sure the session isn't the issue
#guest = Guest.find(session[:guest_id])
end
There needs to be something to handle when #booking is nil - which can happen if I'm reading this right.
def index
if Booking.find(session[:booking_id])?
#booking = Booking.find(session[:booking_id])
else
#booking = Booking.build #or whatever you want here
end
#guest = Guest.find(session[:guest_id])
end