8
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[Rails][View] Layouts and Rendering in Rails

Posted at

1 Overview: How the Pieces Fit Together
This guide focuses on the interaction between Controller and View in the Model-View-Controller triangle. As you know, the Controller is responsible for orchestrating the whole process of handling a request in Rails, though it normally hands off any heavy code to the Model. But then, when it's time to send a response back to the user, the Controller hands things off to the View. It's that handoff that is the subject of this guide.

In broad strokes, this involves deciding what should be sent as the response and calling an appropriate method to create that response. If the response is a full-blown view, Rails also does some extra work to wrap the view in a layout and possibly to pull in partial views. You'll see all of those paths later in this guide.

Railsはlayoutでview(content)をラップし、partial viewをpullしてくる。

2 Creating Responses
From the controller's point of view, there are three ways to create an HTTP response:

Call render to create a full response to send back to the browser
Call redirect_to to send an HTTP redirect status code to the browser
Call head to create a response consisting solely of HTTP headers to send back to the browser

コントローラーから見ると、HTTPレスポンスを返す方法は3つある。

  • renderを使う
  • redirect_toを使ってHTTP redirect status codeを返す
  • headを使う
2.1 Rendering by Default: Convention Over Configuration in Action
You've heard that Rails promotes "convention over configuration". Default rendering is an excellent example of this. By default, controllers in Rails automatically render views with names that correspond to valid routes. For example, if you have this code in your BooksController class:

コントローラーはデフォルトでは、ルートに対応した名前のviewを自動でレンダリングする。

class BooksController < ApplicationController
end
And the following in your routes file:
resources :books
And you have a view file app/views/books/index.html.erb:
<h1>Books are coming soon!</h1>
Rails will automatically render app/views/books/index.html.erb when you navigate to /books and you will see "Books are coming soon!" on your screen.

However a coming soon screen is only minimally useful, so you will soon create your Book model and add the index action to BooksController:
class BooksController < ApplicationController
  def index
    @books = Book.all
  end
end
Note that we don't have explicit render at the end of the index action in accordance with "convention over configuration" principle. The rule is that if you do not explicitly render something at the end of a controller action, Rails will automatically look for the action_name.html.erb template in the controller's view path and render it. So in this case, Rails will render the app/views/books/index.html.erb file.

If we want to display the properties of all the books in our view, we can do so with an ERB template like this:

このようにactionの最後で明示的にviewをレンダリングしない場合、viewsディレクトリからコントローラー名のついたディレクトリを探し、action_name.html.erbテンプレートをレンダリングする。

html.erb
<h1>Listing Books</h1>
 
<table>
  <tr>
    <th>Title</th>
    <th>Summary</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>
 
<% @books.each do |book| %>
  <tr>
    <td><%= book.title %></td>
    <td><%= book.content %></td>
    <td><%= link_to "Show", book %></td>
    <td><%= link_to "Edit", edit_book_path(book) %></td>
    <td><%= link_to "Remove", book, method: :delete, data: { confirm: "Are you sure?" } %></td>
  </tr>
<% end %>
</table>
 
<br />
 
<%= link_to "New book", new_book_path %>
The actual rendering is done by subclasses of ActionView::TemplateHandlers. This guide does not dig into that process, but it's important to know that the file extension on your view controls the choice of template handler. Beginning with Rails 2, the standard extensions are .erb for ERB (HTML with embedded Ruby), and .builder for Builder (XML generator).

実際のレンダリングはActionView::TemplateHandlersのサブクラスで行われる。テンプレートの拡張子によって、使用するテンプレートハンドラーが変わる。

2.2 Using render
In most cases, the ActionController::Base#render method does the heavy lifting of rendering your application's content for use by a browser. There are a variety of ways to customize the behavior of render. You can render the default view for a Rails template, or a specific template, or a file, or inline code, or nothing at all. You can render text, JSON, or XML. You can specify the content type or HTTP status of the rendered response as well.

ActionController::Base#render を利用することで、様々なファイル形式レンダリングできる。フォーマットも text JSON XMLなどが選べる。HTTP statusも設定できる。

If you want to see the exact results of a call to render without needing to inspect it in a browser, you can call render_to_string. This method takes exactly the same options as render, but it returns a string instead of sending a response back to the browser.

render_to_stringを使うことで、レスポンスを返さずに、レンダリングされた結果をみることができる。

2.2.2 Rendering an Action's View
If you want to render the view that corresponds to a different template within the same controller, you can use render with the name of the view:
def update
  @book = Book.find(params[:id])
  if @book.update(params[:book])
    redirect_to(@book)
  else
    render "edit"
  end
end

同一コントローラーだけど、違うアクションのテンプレートを使いたいときは、render "action_name"と書く。

If the call to update fails, calling the update action in this controller will render the edit.html.erb template belonging to the same controller.

If you prefer, you can use a symbol instead of a string to specify the action to render:
def update
  @book = Book.find(params[:id])
  if @book.update(params[:book])
    redirect_to(@book)
  else
    render :edit
  end
end

シンボルでもいい。

2.2.3 Rendering an Action's Template from Another Controller
What if you want to render a template from an entirely different controller from the one that contains the action code? You can also do that with render, which accepts the full path (relative to app/views) of the template to render. For example, if you're running code in an AdminProductsController that lives in app/controllers/admin, you can render the results of an action to a template in app/views/products this way:
render "products/show"
Rails knows that this view belongs to a different controller because of the embedded slash character in the string. If you want to be explicit, you can use the :template option (which was required on Rails 2.2 and earlier):
render template: "products/show"

異なるコントローラーのテンプレートを使いたいときは、render "controller_name/action_name"で指定できる。このときは、app/viewsをベースにパスを解決する。

2.2.8 Rendering JSON
JSON is a JavaScript data format used by many Ajax libraries. Rails has built-in support for converting objects to JSON and rendering that JSON back to the browser:
render json: @product
You don't need to call to_json on the object that you want to render. If you use the :json option, render will automatically call to_json for you.

jsonオプションを使うことで、引数の オブジェクト をJSONに変換して、ブラウザに投げ返せる。

2.2.9 Rendering XML
Rails also has built-in support for converting objects to XML and rendering that XML back to the caller:
render xml: @product
You don't need to call to_xml on the object that you want to render. If you use the :xml option, render will automatically call to_xml for you.

xmlオプションを使うことで、引数の オブジェクト をXMLに変換して、ブラウザに投げ返せる。

2.2.11 Options for render
Calls to the render method generally accept four options:

:content_type
:layout
:location
:status

renderは主に4つのオプションを使用できる。

2.2.11.1 The :content_type Option
By default, Rails will serve the results of a rendering operation with the MIME content-type of text/html (or application/json if you use the :json option, or application/xml for the :xml option.). There are times when you might like to change this, and you can do so by setting the :content_type option:
render file: filename, content_type: "application/rss"

RailsはMIME content-typetext/htmlにしてレスポンスを返す(:json オプションを指定したときはapplication/jsonで、 :xmlオプションを指定したときは application/xml)。
content_types:オプションで設定ができる。

2.2.11.2 The :layout Option
With most of the options to render, the rendered content is displayed as part of the current layout. You'll learn more about layouts and how to use them later in this guide.

You can use the :layout option to tell Rails to use a specific file as the layout for the current action:
render layout: "special_layout"

レンダリングされるコンテンツは通常、current layoutの中身としてレンダリングされる。:layoutオプションを使うことで使用するレイアウトを変更できる。

You can also tell Rails to render with no layout at all:
render layout: false
2.2.11.4 The :status Option
Rails will automatically generate a response with the correct HTTP status code (in most cases, this is 200 OK). You can use the :status option to change this:
render status: 500
render status: :forbidden
Rails understands both numeric status codes and the corresponding symbols shown below.

status:オプションでHTTP status codeを設定できる。

2.2.12 Finding Layouts
To find the current layout, Rails first looks for a file in app/views/layouts with the same base name as the controller. For example, rendering actions from the PhotosController class will use app/views/layouts/photos.html.erb (or app/views/layouts/photos.builder). If there is no such controller-specific layout, Rails will use app/views/layouts/application.html.erb or app/views/layouts/application.builder. If there is no .erb layout, Rails will use a .builder layout if one exists. Rails also provides several ways to more precisely assign specific layouts to individual controllers and actions.

PhotosControllerのアクションから current layoutを探す場合の順番は以下の通り

  • app/views/layouts/photos.html.erb
  • app/views/layouts/photos.builder
  • app/views/layouts/application.html.erb
  • app/views/layouts/application.builder
2.2.12.1 Specifying Layouts for Controllers
You can override the default layout conventions in your controllers by using the layout declaration. For example:
class ProductsController < ApplicationController
  layout "inventory"
  #...
end
With this declaration, all of the views rendered by the products controller will use app/views/layouts/inventory.html.erb as their layout.

コントローラー内でlayoutメソッドを使うと、呼び出すlayoutを変更できる。上の場合、productsコントローラーのどのアクションを使っても、app/views/layouts/inventory.html.erbを利用するようになる。

To assign a specific layout for the entire application, use a layout declaration in your ApplicationController class:
class ApplicationController < ActionController::Base
  layout "main"
  #...
end
With this declaration, all of the views in the entire application will use app/views/layouts/main.html.erb for their layout.

アプリケーション全体に対して、特定のlayoutを指定するには、ApplicationControllerlayoutメソッドを呼べばよい。

2.2.12.2 Choosing Layouts at Runtime
You can use a symbol to defer the choice of layout until a request is processed:
class ProductsController < ApplicationController
  layout :products_layout
 
  def show
    @product = Product.find(params[:id])
  end
 
  private
    def products_layout
      @current_user.special? ? "special" : "products"
    end
 
end
Now, if the current user is a special user, they'll get a special layout when viewing a product.

You can even use an inline method, such as a Proc, to determine the layout. For example, if you pass a Proc object, the block you give the Proc will be given the controller instance, so the layout can be determined based on the current request:
class ProductsController < ApplicationController
  layout Proc.new { |controller| controller.request.xhr? ? "popup" : "application" }
end

layoutにはProcも渡せる。

2.2.12.3 Conditional Layouts
Layouts specified at the controller level support the :only and :except options. These options take either a method name, or an array of method names, corresponding to method names within the controller:
class ProductsController < ApplicationController
  layout "product", except: [:index, :rss]
end
With this declaration, the product layout would be used for everything but the rss and index methods.

exceptやonlyを使うことで、レイアウトの指定範囲を変更できる。

2.2.12.4 Layout Inheritance
Layout declarations cascade downward in the hierarchy, and more specific layout declarations always override more general ones. For example:
application_controller.rb
class ApplicationController < ActionController::Base
  layout "main"
end
posts_controller.rb
class PostsController < ApplicationController
end
special_posts_controller.rb
class SpecialPostsController < PostsController
  layout "special"
end
old_posts_controller.rb
class OldPostsController < SpecialPostsController
  layout false
 
  def show
    @post = Post.find(params[:id])
  end
 
  def index
    @old_posts = Post.older
    render layout: "old"
  end
  # ...
end
In this application:

In general, views will be rendered in the main layout
PostsController#index will use the main layout
SpecialPostsController#index will use the special layout
OldPostsController#show will use no layout at all
OldPostsController#index will use the old layout

layoutはこのように継承関係にある。

2.3 Using redirect_to
Another way to handle returning responses to an HTTP request is with redirect_to. As you've seen, render tells Rails which view (or other asset) to use in constructing a response. The redirect_to method does something completely different: it tells the browser to send a new request for a different URL. For example, you could redirect from wherever you are in your code to the index of photos in your application with this call:
redirect_to photos_url
You can use redirect_to with any arguments that you could use with link_to or url_for. There's also a special redirect that sends the user back to the page they just came from:

renderがレンダリングをしてレスポンスを作るのに対して、redirect_toは特定のURLへ新しくリクエストを投げるように、ブラウザに指示する。redirect_toはlink_toやurl_forと同じ引数をとれる。

redirect_to :back

:backオプションで遷移元のページにリダイレクトする。

2.3.2 The Difference Between render and redirect_to
Sometimes inexperienced developers think of redirect_to as a sort of goto command, moving execution from one place to another in your Rails code. This is not correct. Your code stops running and waits for a new request for the browser. It just happens that you've told the browser what request it should make next, by sending back an HTTP 302 status code.

Consider these actions to see the difference:

redirect_toはブラウザに新しいリクエストを発行させる。この点をrenderと混同しないように注意が必要。

def index
  @books = Book.all
end
 
def show
  @book = Book.find_by(id: params[:id])
  if @book.nil?
    render action: "index"
  end
end
With the code in this form, there will likely be a problem if the @book variable is nil. Remember, a render :action doesn't run any code in the target action, so nothing will set up the @books variable that the index view will probably require. One way to fix this is to redirect instead of rendering:

このコードは@bookがnilのとき、render :actionしてもactionメソッドが実行されるわけではないので、@books変数にはなにも代入されない。もしindexビューで@booksを使っていると、問題がおきる。

def index
  @books = Book.all
end
 
def show
  @book = Book.find_by(id: params[:id])
  if @book.nil?
    redirect_to action: :index
  end
end
With this code, the browser will make a fresh request for the index page, the code in the index method will run, and all will be well.

The only downside to this code is that it requires a round trip to the browser: the browser requested the show action with /books/1 and the controller finds that there are no books, so the controller sends out a 302 redirect response to the browser telling it to go to /books/, the browser complies and sends a new request back to the controller asking now for the index action, the controller then gets all the books in the database and renders the index template, sending it back down to the browser which then shows it on your screen.

While in a small application, this added latency might not be a problem, it is something to think about if response time is a concern. We can demonstrate one way to handle this with a contrived example:

redirect_toにすることで、もう一度新しいリクエストが送られてくるので、さきほどの問題は解決する。

def index
  @books = Book.all
end
 
def show
  @book = Book.find_by(id: params[:id])
  if @book.nil?
    @books = Book.all
    flash.now[:alert] = "Your book was not found"
    render "index"
  end
end
This would detect that there are no books with the specified ID, populate the @books instance variable with all the books in the model, and then directly render the index.html.erb template, returning it to the browser with a flash alert message to tell the user what happened.

renderを使う場合には、一度@booksを初期化してからビューに渡せばよい。こうすることで、リクエストの回数を減らすことができる。

2.4 Using head To Build Header-Only Responses
The head method can be used to send responses with only headers to the browser. It provides a more obvious alternative to calling render :nothing. The head method accepts a number or symbol (see reference table) representing a HTTP status code. The options argument is interpreted as a hash of header names and values. For example, you can return only an error header:
head :bad_request

headメソッドを使うと、headerのみのレスポンスを返す。オプションはheaderの属性名と属性値として解釈される。

This would produce the following header:
HTTP/1.1 400 Bad Request
Connection: close
Date: Sun, 24 Jan 2010 12:15:53 GMT
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8
X-Runtime: 0.013483
Set-Cookie: _blog_session=...snip...; path=/; HttpOnly
Cache-Control: no-cache
Or you can use other HTTP headers to convey other information:
head :created, location: photo_path(@photo)
Which would produce:
HTTP/1.1 201 Created
Connection: close
Date: Sun, 24 Jan 2010 12:16:44 GMT
Transfer-Encoding: chunked
Location: /photos/1
Content-Type: text/html; charset=utf-8
X-Runtime: 0.083496
Set-Cookie: _blog_session=...snip...; path=/; HttpOnly
Cache-Control: no-cache
8
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?