Ruby on Rails, parte 14: CRUD y REST

21 Jun

Ahora que ya tenemos un poco más claro cómo realizar consultas en una base de datos a través de los modelos, podemos continuar con el proyecto que creamos en uno de los anteriores posteos llamado organizador. Pero para eso veremos un poco de teoría antes de comenzar.

CRUD

CRUD, que significa Create Read Update Delete (En español crear, leer, modificar y eliminar) Se utiliza para aquellas interacciones de una aplicación web con una base de datos.

Para trabajar con una base de datos debemos poder crear registros, modificarlos, eliminarlos y mostrarlos, ya sea en lista o individualmente. Esto que parece simple muchas veces se vuelve una tarea repetitiva y que consume mucho tiempo. Por suerte con Rails la cosa es más fácil y ordenada.

REST

RoR tiene la capacidad de recibir cuatro tipos de peticiones:

  • GET
  • POST
  • PUT
  • DELETE

Seguramente ya conozcas GET y POST, pero tal vez te preguntes ¿qué son las otras dos, PUT y DELETE? En realidad los navegadores son capaces de enviar sólo las dos primeras, sin embargo RoR se las ingenia  para detectar cuál de las cuatro peticiones es la que viene desde el navegador. ¿Ahora para qué sirve cada una?

El método GET se utiliza para mostrar información, osea mostrar registros ya sean muchos o uno en particular, POST es para crear nuevos registros en la base de datos, mientras que PUT es para modificarlos y DELETE para eliminarlos. Si bien esto no es obligatorio, es bueno saberlo para tener un orden a la hora de trabajar con peticiones que requieren consultas en una base de datos.

Bien, comencemos. Para facilitarnos esto que acabo de explicar iremos dentro de nuestro proyecto organizador a config -> routes.rb y editamos este archivo, agregando dentro de “Organizador::Application.routes.draw do” esta línea:

resources :tareas

La palabra resources le indicará a nuestra aplicación una serie de acciones relacionada con el REST, en este caso de tareas.

Así que vamos a abrir la consola de Rails y entrar en nuestro proyecto:

cd organizador

Y vamos a escribir lo siguiente:

rake routes

 

Bien, lo que acabamos de hacer nos mostrará la lista de acciones de nuestra aplicación y el método por la cual se accederá a las mismas, ya sea GET, POST, PUT o DELETE.

Petición Ruta Acción Uso
GET /tareas index Aquí deberíamos mostrar la lista de las tareas
GET /tareas/new new Formulario para agregar una nueva tarea.
POST /tareas create Acción para crear una nueva tarea
GET /tareas/:id show Mostrar una tarea individualmente
GET /tareas/:id/edit edit Formulario para modificar una tarea
PUT /tareas/:id update Acción para modificar una tarea
DELETE /tareas/:id destroy Acción para eliminar una tarea

(Fuente: http://guides.rubyonrails.org/routing.html#resource-routing-the-rails-default)

Bien, ya tenemos las rutas preparadas, pero aun nos falta lo más importante que es crear el controlador. Para ello vamos a crear uno llamado Tareas con las siete acciones necesarias. Entonces dentro de la consola escribimos:

rails g controller Tareas index new create show edit update destroy

Primero vamos a ir al archivo routes.rb de nuestro proyecto. Recordar que dentro del raíz está dentro de la carpeta config y buscamos la línea:

# root :to => 'welcome#index'

Y la cambiamos por:

root :to => 'tareas#index'

Ahora para que la acción index sea nuestra página principal vamos a la carpeta public de nuestro raíz y eliminamos el archivo: index.html.

Iniciamos nuestro proyecto desde la consola de comandos:

rails s

Bien, antes que nada vamos dentro de app -> views -> layouts -> application.html.erb y editamos este archivo, que si te acordás es la vista principal de nuestro sitio, el esqueleto del diseño de la página. Antes de la línea:

<%= yield %>

Vamos a pegar el siguiente código:

<h1> Organizador </h1>
<!-- Muestra los mensajes redirigidos por otra página si los hay -->
<% flash.each do |key, value| %>
   <p> <%= value %> </p>
<% end %>

Ingresamos el título de nuestro sitio con un h1, nada nuevo, y con respecto a las líneas:

<% flash.each do |key, value| %>
   <p> <%= value %> </p>
<% end %>

Esto servirá para mostrar mensajes generados de algunas acciones, como por ejemplo después de haber insertado una nueva tarea debe mostrarnos un mensaje con la confirmación de ello. Ya veremos cómo.

Listar registros

Abrimos el controlador que acabamos de crear, Tareas, y modificamos el método index por:

def index
   @tareas = Tarea.all()
end

Dentro de la variable @tareas guardaremos la lista de tareas creadas, sin embargo cambiaremos la lógica del listado. Sólo se listarán las tareas con :activo en true y ordenadas en forma descendente. Así que lo cambiaremos por:

def index
   @tareas = Tarea.select("id, titulo, descripcion").where(:activo => true).order("id DESC");
end

Ahora para probar que funcione correctamente iremos a la vista index.html.erb y agregaremos lo siguiente:

<h2> Lista de tareas </h2>
<p> Crear nueva tarea </p>
<ul>
   <% @tareas.each do |t| %>
   <li>
      <h3> <%= t.titulo %> </h3>
      <p>
         <span> Ver </span>
         |
         <span> Modificar </span>
         |
         <span> Eliminar </span>
      </p>
   </li>
   <% end %>
</ul>

Si querés podés crear un registro manualmente para probar el funcionamiento de la vista. Por el momento sólo se mostrará el listado, los botones de crear, ver, modificar y eliminar no harán nada.

Insertar registros

Por empezar para acceder a la acción del formulario de guardar tareas vamos a modificar dentro de la vista index.html.erb:

<p> Crear nueva tarea </p>

Por esto:

<p> <%= link_to "Crear nueva tarea", new_tarea_path %> </p>

Esto generará un link a la acción new, que es donde se encuentra el formulario para guardar nuevas tareas.

Antes de continuar vamos a la carpeta de los modelos y buscamos el archivo tarea.rb. Vamos a asegurarnos de que los atributos :activo, :descripcion, :prioridad y :titulo sean accesibles para poder insertar y modificar registros. Debería existir esta línea dentro de la clase:

attr_accessible :activo, :descripcion, :prioridad, :titulo

Si no está debemos agregarla.

Ahora nos dirigimos a la carpeta tareas dentro de la carpeta views. Y creamos un nuevo archivo llamado:

_formulario_guardar_tareas.html.erb

Este archivo contendrá el formulario que servirá tanto para crear como modificar tareas. Además dentro de esta carpeta tareas también podemos eliminar las vistas create.html.erb, update.html.erb y destroy.html.erb, ya que esta acciones no necesitarán usarlas.

Volvemos a nuestro controlador tareas y modificamos el método new por:

def new
   @tarea = Tarea.new();
end

Esto creará una nueva instancia de la clase Tarea.

Vamos a la carpeta de vistas de las tareas y modificamos el archivo: _formulario_guardar_tareas.html.erbnew.html.erb.

La vista _formulario_guardar_tareas.html.erb debería quedar así:

<%= form_for @tarea do |f| %> 
   <p> 
      <%= f.label :titulo, "Título" %>
      <br />
      <%= f.text_field :titulo, {:value => @titulo} %>
   </p>
   <p> 
      <%= f.label :descripcion, "Descripción" %>
      <br />
      <%= f.text_area :descripcion, {:value => @descripcion} %>
   </p>
   <p>
      <%= f.label :prioridad, "Prioridad" %>
      <br />
      <%= f.text_field :prioridad, {:value => @prioridad, :size => "1", :maxlength => 1} %>
   </p>
   <p>
      <%= f.submit "Enviar" %> <%= link_to "Cancelar", tareas_path %>
   </p>
<% end %>

Bien, vamos a detenernos un momento. En esta vista para crear un formulario no usaremos el helper form_tag como venimos haciendo hasta ahora, sino que usaremos form_for. La diferencia de este último es que hará referencia a un objeto (@tarea) y no a una acción.

La vista new.html.erb quedará:

<h2> Nueva tarea </h2>
<%= render :partial => "formulario_guardar_tareas.html" %>

La vista _formulario_guardar_tareas.html.erb es un parcial, los parciales son muy útiles para no tener que repetir código. En este caso tanto la vista new.html.erb como edit.html.erb deberán utilizar el mismo formulario para insertar o modifica una tarea, así que ¿para qué repetirlo dos veces?

Luego dentro de la vista new.html.erb con el helper render y el atributo :partial incluimos el parcial.

A tener en cuenta: Los nombres de los archivos que contienen parciales siempre deben escribirse con un guión bajo:

_formulario_guardar_tareas.html.erb

Sin embargo a la hora de llamarlos debe omitirse el guión bajo:

<%= render :partial => "formulario_guardar_tareas.html" %>

Ahora ya tenemos tenemos nuestra acción new con el formulario para ingresar los datos de la tarea nueva, ahora debemos editar la acción save que es la acción de donde llegarán las variables que se enviaron de la primera, new. Así que editaremos el método save:

def create
   #Recuperamos las varibles POST que vinieron desde la acción new.
   @titulo = params[:tarea][:titulo];
   @descripcion = params[:tarea][:descripcion];
   @prioridad = params[:tarea][:prioridad];
   #Creamos el objeto con los valores a ingresar.
   @tarea = Tarea.new({
      :titulo => @titulo,
      :descripcion => @descripcion,
      :prioridad => @prioridad,
      :activo => true
   });
   #Verificamos si la tarea ha podido ser guardado correctamente.
   if @tarea.save()
      redirect_to tareas_path, :notice => "La tarea ha sido insertada";
   else
      render "new";
   end
 end

Bueno, en primer lugar recuperamos las variables que vinieron del anterior formulario. Luego creamos una instancia de la clase Tarea con el valor de las variables y lo ejecutamos con el método save() que nos devolverá true si se ha podido guardar la tarea, pero si ha habido algún problema nos devolverá false.

Si se ha guardado la tarea correctamente entonces redireccionamos a la página index con el mensaje “La tarea ha sido insertada”, de lo contrario cargará la acción new para volver a llenar el formulario.

Recuperar un registro

Ahora nuestra aplicación también debe darnos la posibilidad de cargar una de las tareas para verla en forma individual. Así primero vamos a nuestra vista index.html.erb y modificamos:

<span> Ver </span>

Por:

<%= link_to "Ver", t %>

Este link apuntará a la acción de show con el id de la tarea que queremos ver.

Así que vamos al controlador y modifcamos show por esto:

def show
   @tarea = Tarea.find(params[:id]);
end

Y ahora la vista show.html.erb quedaría así:

<h2> <%= @tarea.titulo %> </h2>
<div>
   <%= simple_format @tarea.descripcion %>
</div>
<p> <%= link_to "Volver", tareas_path %> </p>

 

Modificar registros

Volvamos a nuestra vista index.html.erb y modificamos:

<span> Modificar </span>

Por:

<%= link_to "Editar", edit_tarea_path(t) %>

Esto apuntará a la acción edit, para editar la tarea recuperada por su id. Entonces que ahora vamos a nuestro controlador, al método edit y lo modificamos por:

def edit
   @tarea = Tarea.find(params[:id]);
   @titulo = @tarea.titulo;
   @descripcion = @tarea.descripcion;
   @prioridad = @tarea.prioridad;
end

Y luego a la vista edit.html.erb:

<h2> Modificar tarea </h2>
<%= render :partial => "formulario_guardar_tareas.html" %>

También incluimos el parcial sólo que aquí las variables @titulo, @descripcion y @tarea vendrán cargadas de datos para poder modificarlos.

Ahora sólo falta modificar la acción update que recibirá las variables que vienen desde la acción edit. Reemplazamos el método update de nuestro controlador por:

def update
   @titulo = params[:tarea]["titulo"];
   @descripcion = params[:tarea]["descripcion"];
   @prioridad = params[:tarea]["prioridad"];
   @tarea = Tarea.find(params[:id]);
   @tarea.titulo = @titulo;
   @tarea.descripcion = @descripcion;
   @tarea.prioridad = @prioridad;
   if @tarea.save()
      redirect_to tareas_path, :notice => "La tarea ha sido modificada";
   else
      render "edit";
   end
end

Esta acción será muy parecida a save sólo que no creará un nuevo objeto sino que lo recuperará de la base de datos, modificará los datos con los valores que envió el usuario y si el registro se pudo modificar redireccionará a la acción de index con el mensaje “La tarea ha sido modificada”, sino cargará la acción de edit para volver a cargar el formulario.

Eliminar registros

Abrimos la vista index.html.erb y modificamos:

<span> Eliminar </span>

Por:

<%= link_to "Eliminar", t, :confirm => "Seguro desea eliminar esta tarea?", :method => :delete %>

Esto redireccionará a la acción destroy donde le enviaremos el id de la tarea a eliminar. Así que ahora sólo nos resta modificar el método destroy de nuestro controlador:

def destroy
   @tarea = Tarea.find(params[:id]);
   if @tarea.destroy()
      redirect_to tareas_path, :notice => "La tarea ha sido eliminada";
   else
      redirect_to tareas_path, :notice => "La tarea NO ha podido ser eliminada";
   end
end

 

También nosotros podríamos crear nuestra propia acción manualmente, por ejemplo, una para finalizar la tareas, osea pasarla de estado :activo true a false. Así que iremos a nuestro controlador y agregaremos un nuevo método llamado finalizar:

def finalizar
   @tarea = Tarea.find(params[:id]);
   @tarea.activo = false;
   if @tarea.save()
      redirect_to tareas_path, :notice => "La tarea ha sido finalizada";
   else
      redirect_to tareas_path, :notice => "La tarea NO ha podido finalizar";
   end
end

Luego iremos a la vista index.html.erb y modificaremos:

<%= link_to "Ver", t %>
|
<%= link_to "Editar", edit_tarea_path(t) %>
|
<%= link_to "Eliminar", t, :confirm => "Seguro desea eliminar esta tarea?", :method => :delete %>

Agregando un nuevo enlace:

<%= link_to "Ver", t %>
 |
 <%= link_to "Editar", edit_tarea_path(t) %>
 |
 <%= link_to "Eliminar", t, :confirm => "Seguro desea eliminar esta tarea?", :method => :delete %> 
 |
 <%= link_to "Finalizar", {:action => "finalizar", :id => t}, :confirm => "Seguro desea finalizar esta tarea?" %>

Si ahora probamos pulsar el botón nos dará un error. Para ello vamos a la carpeta raíz y entramos en la carpeta config y modificamos el archivo routes.rb. Debajo de:

resources :tareas

Agregamos la línea:

put "tareas/finalizar"

Esto permitirá que pueda accederse a esta acción y por el método PUT. Pero también debemos crear el alias. Así que en el mismo archivo buscamos la línea:

# match ':controller(/:action(/:id))(.:format)'

Debajo escribiremos lo siguiente:

match 'tareas/finalizar/:id' => 'tareas#finalizar'

Esto nos permitirá que la url:

http://localhost:3000/tareas/finalizar?id=1

Pueda accederse como:

http://localhost:3000/tareas/finalizar/1

(1 es el id de la tarea)

Bueno, hasta acá todo. En este posteo hemos visto lo fácil que se hace trabajar en RoR con los registros de una base. En el próximo posteo veremos como validar los datos antes de ser insertados o modificados.

Saludos!

Anterior: Ruby on Rails, parte 13: Introducción a ActiveRecord

Siguiente: Ruby on Rails, parte 15: Validaciones

Redes sociables

    2 thoughts on “Ruby on Rails, parte 14: CRUD y REST

    1. Buenos días Fernando,

      Muchas gracias por este posteo y por explicar tan claramente el contenido del mismo.

      Tengo una pregunta, cuando estamos recuperando un registro de la tabla Tareas, ¿en qué momento estamos indicándole al link ‘Ver’ que apunte a la acción show? Cualquiera sea tu respuesta, Muchas Gracias, :).

      Saludos,
      Lucía

      • Lucía cómo estás?

        Bien, es una excelente pregunta, aunque un poco difícil de responder. El asunto es así:

        Cuando vos vas a routes.rb y agregás las línea:

        resources :tareas

        En realidad le estás indicando a tu aplicación web que trabajará con un controlador tareas, en plural y con las acciones index, show, new, create, edit, update y destroy. Esas acciones son las que RoR hará que comprenda qué es lo que vos querés hacer, por ejemplo “show” le hará entender a RoR que la acción deberá mostrar un registro. Cuando vos listas las tareas en index a través de un .each() en cada vuelta vos estás recorriendo un objeto de tipo tarea, al cual nosotros le ponemos como un alias “t”:

        < % @tareas.each do |t| %>

        Esa t, es un objeto, por tanto decirle a nuestro código:

        < %= link_to "Ver", t %>

        Es decirle a un link: “vos vas a esta dirección: t” sin embargo, no hay una dirección, hay un objeto t(de tipo tarea), eso a simple vista parece no tener mucho sentido, pero el intérprete de Rails puede comprender que nosotros estamos haciendo un link a un registro, y con qué acción se muestran los registros: con show.

        Otra solución también podría ser reemplazar:

        < %= link_to "Ver", t %>

        Por:

        < %= link_to "Ver", "/tareas/#{t.id}" %>

        Ahí nosotros simplemente le estamos indicando la ruta en forma de string, como se viene acostumbrando en los links desde que hace más de 20 años. Como vos prefieras usarlo está bien.

        Sé que mi respuesta puede resultar un poco pobre o limitada, pero sinceramente muchas de las formas de trabajar de RoR son más mágicas que lógicas. Se podría decir que la línea que puede realizar esta magia es:

        resources :tareas

        Saludos!

    Comments are closed.