[Rails] Why is it render if save is successful and redirect_to fails?

Introduction

During development with Rails, I was worried about how to handle render when implementing the form, so I was able to reconfirm the difference between redirect_to and render while investigating, so I will leave it as a reminder. By the way, I will also write about what I was worried about with render for a moment. (Specifically, handling before_action)

environment

・ Ruby 2.6.5 ・ Rails 6.0.3

Status

I made an application to register restaurant information, and set the following actions on the controller.

shops_controller.rb


 def new
    @shop = Shop.new
 end

 def create
    @shop = @owner.shops.new(shop_params)
    if @shop.save
      redirect_to :index
    else
      render :new
    end
  end

I wrote this without thinking about anything anymore, but if saving fails, why use render to return to the post form again? Let's look back on that.

** In conclusion, I want to return to the input form again while retaining the input contents **.

So why can that be achieved by using render instead of redirect_to? Let's look back at the behavior of redirect_to and render.

What is redirect_to

** redircet_to is a process that allows the client side to access the specified action (or path). ** ** As a result, you will access the page after revisiting the flow of MVC. Specifically, it is a flow of "access the specified action again ⇨ execute the action of the controller ⇨ display the corresponding view".

It does exactly the same thing as accessing an action view from a url.

What is render

Render, on the other hand, just displays the view corresponding to the specified action without going through the ** action again. ** ** Unlike redirect_to, it returns the view without executing the contents of the new action.

The reason why the input contents are retained by render

Let's take a look at the code again based on the explanation above.

shops_controller.rb


 def new
    @shop = Shop.new
 end

 def create
    @shop = @owner.shops.new(shop_params)
    if @shop.save
      redirect_to :index
    else
      render :new
    end
  end

Since the new action is a new registration page for shop information, @ shop = Shop.new is defined in the action. (Create an empty instance) After this, the value entered in the form is passed as params, and it can be saved by successfully passing it as the information of @shop created this time by the create action.

If saving fails (in the case of else), it will be returned to the form again, but if ** redirect_to is used at this time, the new action will be executed again and @shop will become an empty instance again. ** **

On the other hand, if ** render is used, the new action is not used, so @shop will display the form view again while retaining the values of params received by the create action. ** **

haml:new.html.haml



.shop-wrapper
  = form_with model: [@owner, @shop], html: {class: "shopform"}, local: true do |f|

The above is an excerpt of the view, but since the form receives @owner and @shop and displays the contents, if the @shop has contents, the contents will be displayed properly.

That's why you should use render when the create action fails.

The error I encountered this time

By the way, when I set render this time, I first encountered the following error. スクリーンショット 2020-06-26 21.45.44.png

For a moment, this happened, but I set the following before_aciton for the new action.

shops_controller.rb



before_action :set_select_lists, only: [:new]

#Omission

private
def set_select_lists
    @stations = Station.all.map {|station| [station.name, station.id] }.unshift(["Please choose from the following", nil])
    @genres = Genre.all.map {|genre| [genre.name, genre.id] }.unshift(["Please choose from the following(Mandatory)", nil])
    @marks =  Mark.all.map {|mark| [mark.favorite, mark.id] }.unshift(["Please choose from the following(Mandatory)", nil])
  end

I made a pull-down selection list in the form and set it with before_action.

As I wrote earlier, render does not go through the new action again, so ** before_action is not performed → There is no set value and an error occurs in the view display **.

In response to this, for a moment I thought, "If there is a variable that I have to define unexpectedly at @shop, can I use render?", But after thinking for a few minutes, I solved it immediately.

Rewrite the create action as follows.

shops_controller.rb


def create
    @shop = @owner.shops.new(shop_params)
    if @shop.save
    else
      set_select_lists
      render :new
    end
  end

By performing set_select_lists that was done in before_action before rendering with create action, it is possible to render with the value also acquired (render only renders the new view after performing create action). Because it is displayed)

For a moment? ?? ?? But it wasn't such a complicated story.

At the end

Actually, the behavior of redirect_to and render was the place where I first touched programming with Progate and was the most troubled in about a month. When I noticed the difference here, I had the image that my understanding of MVC deepened at the same time.

I hope it will be helpful for beginners like me.

Recommended Posts

[Rails] Why is it render if save is successful and redirect_to fails?
[rails] Difference between redirect_to and render
[Rails] Difference between redirect_to and render [Beginner]
Rails render redirect_to
[Rails] Display error message-Differences between render and redirect_to, flash-
[Rails] What is the difference between redirect and render?
[Rails] Differences between redirect_to and render methods and how to output render methods
Difference between redirect_to and render
Difference between redirect_to and render
Rails is difficult and painful!
Rails is difficult and painful! Ⅱ
Difference between render and redirect_to
Difference between render and redirect_to
[Rails] Save start time and end time
Divide by Ruby! Why is it 0?
Proper use of redirect_to and render