Create or new in Rails controller in create action - ruby-on-rails

In Rails, we define the create action 2 ways. What are the difference?
def create
#shop = Shop.new(params[:shop])
if #shop.save
flash[:success] = 'Thanks for adding new shop.'
redirect_to #shop
else
flash[:error] = 'Error adding review, please try again.'
redirect_to #shop
end
end
# or
def create
#shop = Shop.create(params[:shop])
if #shop.save
flash[:success] = 'Thanks for adding new shop.'
redirect_to #shop
else
flash[:error] = 'Error adding review, please try again.'
redirect_to #shop
end
end
Considering we already have:
def new
#shop = Shop.new
end
Which is more proper?

This def new action is just for the New view (the new action in your Shop controller would correspond to the app/views/shop/new.html.erb file) - it doesn't do any creation:
def new
#shop = Shop.new
end
There is no mention of params[:shop] in that action, because the parameters don't exist yet - that's what you're gathering in the New view.
Your def create action is the action that actually creates the database entry:
def create
#shop = Shop.new(params[:shop])
if #shop.save
flash[:success] = 'Thanks for adding new shop.'
redirect_to #shop
else
flash[:error] = 'Error adding review, please try again.'
redirect_to #shop
end
end
You're using .new instead of .create so that you can do validations. Also, the Shop.new call doesn't actually create the record - it's #shop.save that does that.

If you used Model.create, you don't need to save the object explicitly. The create method will do that for you.
If you used Model.new, you need to save the object by doing #object.save. The new
method does not do that for you.
Using Model.new:
def create
#shop = Shop.new(params[:shop])
if #shop.save
flash[:success] = 'Thanks for adding new shop.'
redirect_to #shop
else
flash[:error] = 'Error adding review, please try again.'
redirect_to #shop
end
end
Using Model.create:
def create
#shop = Shop.create(params[:shop])
# if #shop.save (This is not required)
if #shop
flash[:success] = 'Thanks for adding new shop.'
redirect_to #shop
else
flash[:error] = 'Error adding review, please try again.'
redirect_to #shop
end
end

The first way doesn't do what you expect:
def create
#shop = Shop.new(params[:shop]) # This won't create a new record on Shops table unless...
#show.save # ...you do this
end
def create
#shop = Shop.create(params[:shop]) # This will create a new record if everything is fine
if #shop.save # This is redundant
# ...
end
end
Calling create and then save is redundant. The create method will try to make a new record and fail silently if validations are not successful. In the other hand, save will try to make a new record, but returns nil if vaildations fail, so you can use it in a if/else block.

In create controller action, Shop.new is useless without following #shop.save. Usually it's being split into these two steps to handle validation errors. We init shop with data from the user. If data is ok, we save the shop. If there are validation errors, we tell user to try again.
Or we need to do some complex attribute initialisation before we save record to the database.
#user = User.new params[:user]
#user.reputation = fetch_reputation_from_stackoverflow
if #user.save
# the usual steps here
RE: question edit
Considering we already have:
def new
#shop = Shop.new
end
What is there in new action is completely irrelevant. If your validations might fail (or otherwise your model might not be created successfully), then use new + save pair. If you are sure that input data is ok and model will always save, then use only create (following save is redundant).

Related

Use different variable depending on which view was used in Rails 4

I'm using Mailboxer so that users can reply to posts in my site. There are two types of post models in my site which users might reply to, Requests and Offers.
Here is my messages controller:
class MessagesController < ApplicationController
# GET /message/new
def new
#request = Request.find(params[:request])
#message = current_user.messages.new
#user = #request.user
end
def reply
#conversation ||= current_user.mailbox.conversations.find(params[:id])
end
# POST /message/create
def create
#user = User.find(params[:user])
#body = params[:body]
#subject = params[:subject]
current_user.send_message(#user, params[:body], params[:subject])
flash[:notice] = "Message has been sent!"
redirect_to :conversations
end
end
In the new action, I'm using #request, but if I reply to an Offer post, I'll need to use an #offer variable. Is there a way I can use an if statement to choose between an #offer and #request depending on the view the new action is called from? Is this the best way to go about this?
It's not the most graceful thing in the world but something as simple as this should work.
if params[:request]
#request = Request.find(params[:request])
else
#offer = Offer.find(params[:offer])
end

TypeError: can't cast LinkedIn::Mash to text Error

I am trying to save data from calling the Linkedin API in my rails app, but when it goes to save I get the error:
TypeError: can't cast LinkedIn::Mash to text
I do not want the user to edit the information either, so I set the information in the NEW controller as follows:
def new
#client = LinkedIn::Client.new
#client.authorize_from_access(current_user.consumer_token, current_user.consumer_secret)
#resume = Resume.new do |u|
u.location = current_user.location
u.picture_url = current_user.image_url
u.summary = #client.profile(:fields => %w(summary))
u.work_experience = #client.profile(:fields => %w(positions))
u.education = #client.profile(:fields => %w(educations))
u.user = current_user
end
#resume.save!
if #resume.save
flash[:succes] = "Resume Saved!"
redirect_to resumes_path
else
flash[:error] = "Resume did not save."
redirect_to resumes_path
end
end
Is that considered bad practice? Should I set the save in the create controller? How about the TypEerror? I feel like since I am saving the information straight from the API that it can't be saved as plain test.
This is what your config/routes.rb should look like:
resources :resumes
This is the #create method in your controller.
def create
#resume = Resume.new(params[:resume]) # if you are on rails 4 then you have to use strong params
if #resume.save
flash[:succes] = "Resume Saved!"
redirect_to resumes_path
else
flash[:error] = "Resume did not save."
redirect_to resumes_path
end
end
The #create method is a post naturally. This resource is very helpful for understanding routes. http://guides.rubyonrails.org/routing.html. As to your question regarding the mash, I do think it has been answered here How to parse a Mash from LinkedIn to create a Ruby object.

Updating a record via a action method

User signs up, is redirected to a page to be collected info, pretty straight forward
I for my life can't figure out how to do this
My controller for the user
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def additional_info
#user = User.find session[:user_id]
#user = User.update(user_addinfo)
redirect_to user_path
end
def create
#user = User.new(user_params)
if #user.save
#session[:user_id] = #user.id
#UserMailer.welcome_email(#user).deliver
sign_in #user
redirect_to additional_info_path
flash[:success] = "Welcome to InYourShoes!"
else
render'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
def user_addinfo
params.require(:user).permit(:year)
end
end
user_addinfo is the action method that i want to call updating my record on for my additional_info method.
the def create method has commented line that i'm unsure if necessary, particularly the session[:user_id] = #user.id. I was told that i need this in order to keep track of my session, but perhaps someone can debunk this for me, as im following michael hartl's tutorial.
as of right now with this code, rails is giving me a parameter missing in the
params.require(:user).permit(:year) line.
Much help is greatly appreciated. Ive been trying many different things, and cant seem to figure this out
Change your controller code as below:
def additional_info
#user = User.find params[:id] ## Set #user
end
def update
if #user.update(user_addinfo)
redirect_to user_path(#user), notice: 'User was successfully updated.'
else
render action: 'additional_info'
end
end
def create
#user = User.new(user_params)
if #user.save
#session[:user_id] = #user.id
#UserMailer.welcome_email(#user).deliver
sign_in #user
redirect_to additional_info_path(#user) ## Pass #user
flash[:success] = "Welcome to InYourShoes!"
else
render'new'
end
end
and in your routes.rb update the additional_info route as
get 'info/:id' => 'users#additional_info', :as => 'additional_info'
You additional_info action seems to be wrong. You need to pass in the id of the user for whom you are collecting additional information.
def additional_info
#user = User.find params[:id]
#user.update_attributes(user_addinfo)
redirect_to user_path(#user)
end
The line you have commented in your create method:
#session[:user_id] = #user.id
Is what is storing the user id to a session variable and not a param in the url.
You then have this line commented in your additional_info method
#user = User.find session[:user_id]
This is looking up the user by the id that you would have previously stored in the session variable.
At that point the user object would be stored in user
If you need it in your instance variable, make sure to modify the line to be
#user = User.find session[:user_id]
Your user would then be stored in #user and be able to be accessed in the view

Rails: attributes not being saved even though I called #user.save

I'm running this function, and I KNOW that it gets called because the redirect_to is working. But for some reason, #user isn't! If it helps, #user is devise based.
def make_feed_preference
#user = current_user
##user.feed_preference = params[:preference]
#user.feed_preference = "time"
#user.name = "Shoo Nabarrr"
#user.karma = 666
#user.save
redirect_to '/posts'
end
I fixed it myself. I had to create a new class attached to users in order to get it to work. Lol.
Do you have any validations on this user? They are probably blocking this save. The redirect_to will be called regardless of whether or not the save passes or fails.
I would recommend doing it like this instead:
if #user.save
redirect_to '/posts'
else
render :feed_preference
end
Where :feed_preference is the form where users enter their feed preferences.
There are cases where I want to be sure to update a flag or other field on a record even if the record has validation problems. (However, I would never do that with unvalidated user input.) You can do that thusly:
def make_feed_preference
case params[:preference]
when 'time', 'trending_value', 'followers'
current_user.update_attribute 'feed_preference', params[:preference]
flash[:notice] = 'Your feed preference has been updated.'
else
flash[:notice] = 'Unknown feed preference.'
end
redirect_to '/posts'
end

Rails: How do you create a new nested resource?

In my app, Users have Conversations, and Conversations have Messages. A Message belongs both to a User (as an author) and to a Conversation.
I want to create a new Message. This is the code I'm using right now in MessagesController.
def new
#user = current_user #currently logged in user
#conversation = Conversation.find(params[:id])
#message = #conversation.messages.build
end
def create
#conversation = Conversation.find(params[:conversation_id])
#message = #conversation.messages.build(params[:message])
if #message.save
redirect_to username_conversation(current_user, #message.conversation)
else
redirect_to root_url
end
end
params[:message] contains the message content ("content" => "I'm Spartacus").
This isn't working (probably because I'm not specifying the user/author when creating a new Message?). How do I make this work the Rails way?
Thanks.
You need to set the user manually. Only one property can be set using the short methods provided by Rails.
def new
#user = current_user #currently logged in user
#conversation = Conversation.find(params[:id])
#message = #conversation.messages.build
#message.user = user
end
def create
#conversation = Conversation.find(params[:conversation_id])
#message = #conversation.messages.build(params[:message])
#message.user = current_user
if #message.save
redirect_to username_conversation(current_user, #message.conversation)
else
redirect_to root_url
end
end

Resources