Recursos REST
REST es una forma particular de definir las rutas HTTP para hacer el CRUD de los modelos en nuestra aplicación Web.
A los modelos también se les conoce como recursos.
Por ejemplo, las rutas para hacer el CRUD de un modelo
Product
serían las siguientes:Verbo | Path | Controlador#Acción | Descripción |
---|---|---|---|
GET | /products | products#index | muestra la lista de productos |
GET | /products/new | products#new | muestra el formulario para crear un producto |
POST | /products | products#create | crea un nuevo producto |
GET | /products/:id | products#show | muestra los detalles de un producto |
GET | /products/:id/edit | products#edit | muestra el formulario para editar un producto |
PATCH/PUT | /products/:id | products#update | actualiza un producto |
DELETE | /products/:id | products#destroy | eliminar un producto |
Para definir las rutas de un recurso en
config/routes.rb
se utiliza la palabra resources
:resources :products
La línea anterior es equivalente a las siguientes 8 rutas:
get "/products", to: "products#index"
get "/products/new", to: "products#new"
post "/products", to: "products#create"
get "/products/:id", to: "products#show"
get "/products/:id/edit", to: "products#edit"
put "/products/:id", to: "products#update"
patch "/products/:id", to: "products#update"
delete "/products/:id", to: "products#destroy"
Para actualizar un recurso podemos utilizar el verbo
PUT
o PATCH
.Así como desde la consola podemos generar un modelo y un controlador, existe un generador que nos permite crear un recurso, que genera el modelo y el controlador.
Por ejemplo, para generar el recurso de productos, es decir, el modelo
Product
y el controlador ProductsController
utilizaríamos el siguiente comando:$ rails g resources products name description:text price:decimal
Ese comando genera, entre otros, lo siguiente:
- El modelo
Product
enapp/models/product.rb
- La migración que se va a utilizar para crear la tabla.
- El controlador en
app/controllers/products_controller.rb
- El archivo
products.scss
enapp/assets/stylesheets/
. - El archivo
products.coffee
enapp/assets/javascripts/
. - La línea
resources :products
enconfig/routes.rb
.
No olvides correr la migración:
$ rails db:migrate
El controlador de un recurso está compuesto de las siguientes acciones (métodos):
index
para mostrar la lista de registros.show
para mostrar los detalles de un registro.new
para mostrar el formulario de creación.create
para crear un registro.edit
para mostrar el formulario de edición.update
para actualizar un registro.destroy
para eliminar un registro.
Veamos cómo se implementaría cada una de estas acciones para un modelo
Product
.El método
index
en el controlador ProductsController
quedaría de la siguiente forma:def index
@products = Product.all
end
Y la vista en
app/views/products/index.html.erb
:<h1>Productos</h1>
<table>
<thead>
<tr>
<th>Nombre</th>
<th>Descripción</th>
<th>Precio</th>
<th></th>
</tr>
</thead>
<tbody>
<% @products.each do |product| %>
<tr>
<td><%= product.name %></td>
<td><%= product.description %></td>
<td><%= product.price %></td>
<td>
<%= link_to "Mostrar", product %>
<%= link_to "Editar", edit_product_path(product) %>
<%= link_to "Eliminar", product, method: :delete, data: { confirm: "¿Estás seguro de eliminar este producto?" } %>
</td>
</tr>
<% end %>
</tbody>
</table>
El método
show
quedaría de la siguiente forma:def show
@product = Product.find(params[:id])
end
Y la vista en
app/views/products/show.html.erb
quedaría de la siguiente forma:<h1>Detalles del Producto</h1>
<div>Nombre: <%= @product.name %></div>
<div>Descripción: <%= @product.description %></div>
<div>Precio: <%= @product.price %></div>
La creación de un registro se divide en dos: la ruta que muestra el formulario y la ruta que inserta el registro en la base de datos cuando alguien llena el formulario.
Para mostrar el formulario se utiliza el método
new
que se implementaría de la siguiente forma:def new
@product = Product.new
end
Y la vista en
app/views/products/new.html.erb
sería la siguiente:<%= form_for @product do |f| %>
<div>
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div>
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div>
<%= f.label :price %><br>
<%= f.number_field :price %>
</div>
<div>
<%= f.submit %>
</div>
<% end %>
Para crear el registro necesitamos implementar el método
create
y un método de ayuda product_params
:def create
@product = Product.new(product_params)
if @product.save
redirect_to products_path
else
render :new
end
end
private
def product_params
params.require(:product).permit(:name, :description, :price)
end
La razón por la que tuvimos que crear ese método de ayuda es por un tema de seguridad. Ese método está filtrando la información que queremos que se pueda cambiar directamente. A esto se le conoce como strong parameters.
No necesitamos una vista para esta acción.
Para mostrar el formulario de editar se utiliza el método
edit
que se implementaría de la siguiente forma:def edit
@product = Product.find(params[:id])
end
Y la vista en
app/views/products/edit.html.erb
sería la siguiente:<%= form_for @product do |f| %>
<div>
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div>
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div>
<%= f.label :price %><br>
<%= f.number_field :price %>
</div>
<div>
<%= f.submit %>
</div>
<% end %>
El formulario de edición es igual al de creación, así que una buena práctica es moverlo a un partial
_form.html.erb
.Para actualizar el registro necesitamos implementar el método
update
y el método de ayuda product_params
que ya creamos previamente:def update
@product = Product.find(params[:id])
if @product.update(product_params)
redirect_to products_path
else
render :edit
end
end
No necesitamos una vista para esta acción.
Para eliminar un registro necesitamos implementar el método
destroy
:def destroy
product = Product.find(params[:id])
product.destroy
redirect_to products_path
end
Para limitar las rutas y acciones de un recurso utiliza la opción
only
o except
de resources
en config/routes.rb
:resources products, only: [:index, :new, :create]
resources articles, except: [:destroy]
En este caso, para products sólo se generarían 3 rutas: la de listar, la de mostrar el formulario y la de crear.
Para resources se generarían todas excepto la de eliminar.
Para mostrar notificaciones de éxito al crear, editar y eliminar registros utilizando el
flash
.Por ejemplo, para mostrar una notificación de éxito al eliminar un producto:
def destroy
product = Product.find(params[:id])
product.destroy
flash[:notice] = "El producto ha sido eliminado exitosamente"
redirect_to products_path
end
El
notice
también se puede agregar directamente sobre el redirect_to
:redirect_to products_path, notice: "El producto ha sido eliminado exitosamente"
Puedes agregar el siguiente código donde quieres que aparezcan los mensajes de flash en el layout
app/views/layouts/application.html.erb
:<% flash.each do |name, msg| -%>
<%= content_tag :div, msg, class: name %>
<% end -%>
Puedes ejecutar métodos que se ejecutan antes o después de una acción de un controlador utilizando
before_action
y after_action
.Por ejemplo, podemos verificar si los usuarios ya están autenticados con un
before_action
:class ApplicationController < ActionController::Base
before_action :require_login
private
def require_login
unless logged_in?
flash[:error] = "You must be logged in to access this section"
redirect_to new_login_url # halts request cycle
end
end
end
Si haces un
render
o redirect_to
en un before_action
la acción no se ejecuta.Los filtros son heredados, así que si defines el filtro en
ApplicationController
se va a ejecutar en todos los controladores de la aplicación.Puedes limitar las acciones a las que aplica un filtro utilizando las opciones
only
o except
.Por ejemplo:
before_action :require_login, only: [:new, :create]
before_action :require_login, except: [:index]
Last modified 4mo ago