When i use scaffold in rails , controller creates various methods like
new,create,show,index etc
but here i can't understand transition of new action to create action
eg. when i click on new Post it look up for new action,now it render _form,but when at time of submit how data entered to that particular table, where the create action of controller called and how ?
My posts_controller is as
def new
#post = Post.new
#post.user_id = current_user.id
#post.save
respond_to do |format|
format.html # new.html.erb
format.json { render json: #post }
end
end
# GET /posts/1/edit
def edit
#post = Post.find(params[:id])
authorize! :manage, #post
end
# POST /posts
# POST /posts.json
def create
#post = Post.new(params[:post])
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render json: #post, status: :created, location: #post }
else
format.html { render action: "new" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
By default scaffolding on form (read here)
When the user clicks the Create Post button on this form, the browser
will send information back to the create action of the controller
(Rails knows to call the create action because the form is sent with
an HTTP POST request; that’s one of the conventions that were
mentioned earlier):
def create
#post = Post.new(params[:post])
respond_to do |format|
if #post.save
format.html { redirect_to(#post,
:notice => 'Post was successfully created.') }
format.json { render :json => #post,
:status => :created, :location => #post }
else
format.html { render :action => "new" }
format.json { render :json => #post.errors,
:status => :unprocessable_entity }
end
end
end
If you want customize an action on new form scaffold, you should add :url => {:action => "YourActionName"} on your form.
Example :
#form
form_for #post, :url => {:action => "YourActionName"}
#controller
def YourActionName
#post = Post.new(params[:post])
respond_to do |format|
if #post.save
format.html { redirect_to(#post,
:notice => 'Post was successfully created.') }
format.json { render :json => #post,
:status => :created, :location => #post }
else
format.html { render :action => "new" }
format.json { render :json => #post.errors,
:status => :unprocessable_entity }
end
end
end
#route
match '/posts/YourActionName`, 'controllers#YourActionName', :via => :post
It's all about HTTP verbs and routes.
Your form will make a POST request to the /posts route. If you list your routes using rake routes, you'll see that all POST requests to that specific route are being directed to the create action in PostsController, or posts#create for short.
When you point your browser to /posts/new, it renders the new action, which presents you with a form to fill out (defined in app/views/posts/new.html.erb and app/views/posts/_form.html.erb. When you click the Submit button in your form, it posts your data to the create action, which actually creates the record in the database.
Looking at your PostsController code, you probably don't want to have the line
#post.save
in your new action, since that will save a blank record to the database - whether the user completes the form or not. And, you'll probably want to move
#post.user_id = current_user.id
to your create action, since that is where you're actually saving the post to the database.
Related
(I've broken out the 2nd question that originally was part of this post into a separate post)
I am creating a product landing page with Rails in which users can enter their email address to be notified when the product launches. (Yes, there are services/gems etc that could do this for me, but I am new to programming and want to build it myself to learn rails.)
On submit of the form, if there are errors, the app currently redirects to '/invites' I would like to instead display error messages on the same page/URL as the original form? (In my case, the form is located at root while the error messages are displaying at '/invites')
I have read the Rails Guide on Routes and numerous stackoverflow posts on handling form errors nothing I've found seems to answer the question I have.
Update: Based on the reply from #rovermicrover I would like to clarify that, while I'm open to an Ajax solution, I'm fine with a page refresh that displays the error message. (I was not able to get the recommendation by #rovermicrover to function as desired - see my response to that solution below for more details.)
What I did:
Invite model:
class Invite < ActiveRecord::Base
attr_accessible :email
validates :email, :presence => {:message => "Please enter an email address."}
end
My routes file:
SuggestionBoxApp::Application.routes.draw do
root to: 'invites#new'
resources :invites
end
This is what I have in the Invites controller (I've only included the actions I'm referencing: new, create, show - it's basically the default of what Rails might generate):
class InvitesController < ApplicationController
def show
#invite = Invite.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #invite }
end
end
def new
#invite = Invite.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #invite }
end
end
def create
#invite = Invite.new(params[:invite])
respond_to do |format|
if #invite.save
format.html { redirect_to #invite }
format.json { render json: #invite, status: :created, location: #invite }
else
format.html { render action: "new" }
format.json { render json: #invite.errors, status: :unprocessable_entity }
end
end
end
end
Please let me know if there is any additional info I can provide in helping to answer this question. Thanks!
Make the form 'remote'
form_for #invite, :remote => true
....
Then in the controller
def create
#invite = Invite.new(params[:invite])
respond_to do |format|
if #invite.save
format.html { redirect_to #invite }
format.js { render :action => 'create_suc'}
else
format.html { render action: "new" }
format.js { render :action => 'create_fail' }
end
end
end
/invites/create_suc.js.erb
$('#errors').remove()
$('#new_invite').prepend("<div class='Thanks'>Thanks for signing up</div>")
$('#new_invite').hide("")
/invites/create_fail.js.erb
$('#new_invite').html('<%= escape_javascript render("form", :invite => #invite) %>');
Forms is a partial with your.... form in it, and also the handling of all errors on #invite.
There is a way to do this without resorting the making the form submit "remote", from a pure Ruby on Rails perspective. However, you can do this only if the browser has enabled cookies.
The idea is to save the form data in the session information in case of an error.
Just remember to delete the session data in case of success.
def new
#invite = Invite.new(session[:invite])
respond_to do |format|
format.html # new.html.erb
format.json { render json: #invite }
end
end
def create
#invite = Invite.new(params[:invite])
respond_to do |format|
if #invite.save
session.delete(:invite)
format.html { redirect_to #invite }
format.json { render json: #invite, status: :created, location: #invite }
else
session[:invite] = params[:invite]
format.html { render action: "new" }
format.json { render json: #invite.errors, status: :unprocessable_entity }
end
end
end
I understand that I cannot POST on an HTML redirect, but my situation requires that I redirect to create action after authenticating user. I would like to know how to bypass this restriction:
In particular, I would like to allow an user to fill out a post without logging in using Omniauth. I save the post to session[:post] using an AJAX call. Then, the user can login using omniauth and persist the post.
I have a PostsController with create action that handle initial ajax call, and also handle html request after authenticating user:
def create
#post = Post.new(params[:post])
respond_to do |format|
format.html{
if #post.save
redirect_to #post, notice: 'Post was successfully created.'
else
render action: "new"
end
}
format.json {
if session[:post] = #post
render json: #post, status: :created, location: #post
else
render json: #post.errors, status: :unprocessable_entity
end
}
end
end
end
Then, in my controller that handles callback from Facebook, I have:
class ServicesController < ApplicationController
def create
... authentication logic here ...
sign_in(:user, service.user)
redirect_to :controller => "posts", :action =>"create"
end
method_alias: :facebook, :create
However, this doesn't work, because I can't redirect to a "create" action. How can I accomplish this task?
In the code you posted, you never read the content of the session. I think it can work if you change your code with this :
Change initialization of #post:
#post = Post.new(params[:post]) || session[:post] # Find object in session if not
And add after post.save :
session.delete :post # clean session after successful creation
New full method:
def create
#post = Post.new(params[:post]) || session[:post] # Find object in session if not in params
respond_to do |format|
format.html{
if #post.save
redirect_to #post, notice: 'Post was successfully created.'
session.delete :post # clean session after successful creation
else
render action: "new"
end
}
format.json {
if session[:post] = #post
render json: #post, status: :created, location: #post
else
render json: #post.errors, status: :unprocessable_entity
end
}
end
end
end
You can create a method on a Post model and call it both from PostsController and ServicesController to save the post (though in this case it's quite trivial: new, then save, so you achieve nothing in terms of DRY, may be some encapsulation benefits). Or create a common mixin containing the create_post method with all the logic. Then and include it into SessionsController and PostsController and call it from 'create'.
In the mixin module:
def create_post(allow_json=false)
#post = Post.new(params[:post])
respond_to do |format|
format.html {
if #post.save
redirect_to #post, notice: 'Post was successfully created.'
else
render "posts/new"
end
}
if allow_json
... your post-saving & json-processing logic ...
end
end
end
In PostsController:
def create
create_post(true)
end
In SessionsController:
def create
... authentication logic here ...
sign_in(:user, service.user)
create_post(false)
end
I didn't compile and try, so I only hope it works. In general, I must say there's something basically wrong, so I'd look for other architectural solutions to achieve the same results, but as a quick-and-dirty approach it should probably work.
I found a hack to avoid this issue: I let #create handling AJAX call and write to session, then create another action to persist it into my database after user get authenticated:
class PostsController < ApplicationController
def create
#post = Post.new(params[:post])
respond_to do |format|
format.json {
if session[:post] = #post
render json: #post, status: :created, location: #post
else
render json: #post.errors, status: :unprocessable_entity
end
}
end
end
def persist
#post = session[:post]
if #post.save
session.delete :post
redirect_to #post, notice: 'Post was successfully created.'
else
render action: "new"
end
end
Then in my routes.rb, I have:
resources :posts do
collection do
get 'persist'
end
end
Finally, in my ServicesController:
sign_in(:user, service.user)
redirect_to persist_posts_path
I want my page to refresh once a record has been created, at the moment it directs it to the page before. here is the code from my controller:
def create
#license = License.new(params[:license])
respond_to do |format|
if #license.save
format.html { redirect_to :controller => 'customers', :action => 'index' }
format.json { render json: #customer, status: :created, location: #customer }
else
format.html { render action: "new" }
format.json { render json: #customer.errors, status: :unprocessable_entity }
end
end
where it says redirect_to i need that to refresh, or link to the current page, with the current id, which would be :controller => 'customers', :action => 'show' but with the id of the current page's record.
Try
redirect_to customer_path(#license.id)
instead.
Depending on what your routes.rb file says, it should work.
But if it doesn't, try:
redirect_to show_customer_path(#license.id)
However, here I have to assume that somehow, your customers_controller.rb is somehow showing records from the License model. If License and Customer are separate models, you will have to find the customer_id in some other way.
Perhaps, it is:
redirect_to customer_path(#license.customer_id)
If License is not connected to Customer in any way, you will need to pass it in as part of the post request.
Try,
I think you are passing customer_id to on params(Licensee belongs to customer), if so then
redirect_to customer_path(#license.customer) or redirect_to customer_path(params[:customer_id])
I'm diving into RoR and as I'm going through the tutorials, scaffolds, and docs, I'm coming across some code that confuses me. For example, I just read up on the 'redirect_to' method, but the guide I read didn't cover the example of redirecting to an instance var, such as the code that is generated in a typical scaffold...
# POST /articles
# POST /articles.xml
def create
#article = Article.new(params[:article])
respond_to do |format|
if #article.save
format.html { redirect_to(#article, :notice => 'Article was successfully created.') }
format.xml { render :xml => #article, :status => :created, :location => #article }
else
format.html { render :action => "new" }
format.xml { render :xml => #article.errors, :status => :unprocessable_entity }
end
end
end
In the statement format.html { redirect_to(#article, :notice => 'Article was successfully created.') }, the code is redirecting to the instance var article, which causes a 'redirect_to' the show method in the current controller. Why does this cause it to redirect to the show method?
Thanks so much for your help!
Why does this cause it to redirect to the show method?
Because if you don't specify particular action, Rails assumes you want to 'show' object. If you have another action in mind, try
redirect_to :action => :do_something, :id => #article
When I:
self.save
or
save!
in a model it automatically redirects me to the show view for that given model. How can I override this? I want to save the model and then go to another action/view in the same controller.
In particular, I have a process where I save a members details, and then I want to continue the process by going to the next page, e.g. payment page, that uses the id of the saved model.
In your controller you might have a block like:
def create
#user = User.new(params[:place])
respond_to do |format|
if #user.save
format.html { redirect_to(#user, :notice => 'User was successfully created.') }
format.xml { render :xml => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
You can change the target of the redirect_to (after format.html) from here - at present it is directing you to the record for that user, ie. #user. Take a look at http://api.rubyonrails.org/classes/ActionController/Base.html for a bit more info.
You likely have a block like this in your create/update methods:
respond_to do |format|
if #post.save
format.html { redirect_to(#post, :notice => 'Post was successfully created.') }
format.xml { render :xml => #post, :status => :created, :location => #post }
else
format.html { render :action => "new" }
format.xml { render :xml => #post.errors, :status => :unprocessable_entity }
end
end
So if your instance variable is named #post, and it's redirecting to the show view for the post after it saves, all you have to do is change the "redirect_to(#post, ..." part to whatever you want. Say you wanted to redirect to the root of your site - you could instead have
redirect_to(root_path, :notice => 'Post was successfully created.')
In your particular case, you could use something like this if you have your routes set up:
redirect_to(payment_page_path(#post), :notice => 'Post was successfully created.')
Hope that helps!
if you call save from your Model you will not be directed anywhere, it just does a direct model access save to the database. Your redirections are described in your controller in your create and update actions. you can find a list of routes by running rake routes and then pick the path you want your app to render when you save your model instance. you may have a route called payment_path which might look like this in your controller
map.payment :controller => :payments_controller, :action => index
and you would say in your create action
def create
if #item.save(params[:item])
redirect_to payment_path
else
flash[:error] = "there was a problem"
render :action => buy
end
end
if you need to pass a param, like user id to your route, then you need to include that in the path parameters
redirect_to payment_path(#user) #=> automagically finds the id of active record models