Cómo Crear Recursos Anidados para una Aplicación Ruby on Rails

Introducción

Ruby on Rails es un marco de aplicación web escrito en Ruby que ofrece a los desarrolladores un enfoque con opiniones sobre el desarrollo de aplicaciones. Trabajar con Rails proporciona a los desarrolladores:

  • Convenciones para manejar cosas como el enrutamiento, datos con estado y gestión de activos.
  • A firm grounding in the model-view-controller (MCV) architectural pattern, which separates an application’s logic, located in models, from the presentation and routing of application information.

A medida que agregas complejidad a tus aplicaciones de Rails, es probable que trabajes con varios modelos, que representan la lógica empresarial de tu aplicación e interactúan con tu base de datos. Agregar modelos relacionados implica establecer relaciones significativas entre ellos, lo que luego afecta cómo se transmite la información a través de los controladores de tu aplicación y cómo se captura y presenta de vuelta a los usuarios a través de las vistas.

En este tutorial, trabajarás en una aplicación de Rails existente que ofrece a los usuarios datos sobre tiburones. Esta aplicación ya tiene un modelo para manejar datos de tiburones, pero agregarás un recurso anidado para publicaciones sobre tiburones individuales. Esto permitirá a los usuarios desarrollar un amplio conjunto de pensamientos y opiniones sobre tiburones individuales.

Prerrequisitos

Para seguir este tutorial, necesitarás:

  • A local machine or development server running Ubuntu 18.04. Your development machine should have a non-root user with administrative privileges and a firewall configured with ufw. For instructions on how to set this up, see our Initial Server Setup with Ubuntu 18.04 tutorial.
  • Node.js y npm instalados en tu máquina local o servidor de desarrollo. Este tutorial utiliza la versión de Node.js 10.16.3 y la versión de npm 6.9.0. Para obtener orientación sobre cómo instalar Node.js y npm en Ubuntu 18.04, sigue las instrucciones en la sección “Instalación utilizando un PPA” de Cómo instalar Node.js en Ubuntu 18.04.
  • Ruby, rbenv y Rails instalados en tu máquina local o servidor de desarrollo, siguiendo los Pasos 1-4 en Cómo instalar Ruby on Rails con rbenv en Ubuntu 18.04. Este tutorial utiliza Ruby 2.5.1, rbenv 1.1.2, y Rails 5.2.3.
  • SQLite instalado y una aplicación básica de información sobre tiburones creada, siguiendo las instrucciones en Cómo construir una aplicación Ruby on Rails.

Paso 1 — Creación de la estructura básica del modelo anidado

Nuestra aplicación aprovechará las asociaciones de Active Record para establecer una relación entre los modelos Shark y Post: las publicaciones pertenecerán a tiburones particulares, y cada tiburón puede tener múltiples publicaciones. Por lo tanto, nuestros modelos Shark y Post estarán relacionados a través de las asociaciones belongs_to y has_many.

El primer paso para construir la aplicación de esta manera será crear un modelo Post y los recursos relacionados. Para hacer esto, podemos usar el comando rails generate scaffold, el cual nos dará un modelo, una migración de base de datos para alterar el esquema de la base de datos, un controlador, un conjunto completo de vistas para gestionar operaciones estándar de Crear, Leer, Actualizar y Borrar (CRUD), y plantillas para parciales, ayudantes y pruebas. Necesitaremos modificar estos recursos, pero usar el comando scaffold nos ahorrará algo de tiempo y energía ya que genera una estructura que podemos utilizar como punto de partida.

Primero, asegúrate de que estás en el directorio sharkapp del proyecto Rails que creaste en los requisitos previos:Crea tus recursos de Post con el siguiente comando:

  1. cd sharkapp

Crea tus recursos Post con el siguiente comando:

rails generate scaffold Post body:text shark:references

Con body:text, estamos indicando a Rails que incluya un campo body en la tabla posts de la base de datos, que se relaciona con el modelo Post. También estamos incluyendo la palabra clave :references, que establece una asociación entre los modelos Shark y Post. Específicamente, esto garantizará que una clave foránea que representa cada entrada de tiburón en la base de datos sharks se agregue a la base de datos posts.

Una vez que hayas ejecutado el comando, verás la salida confirmando los recursos que Rails ha generado para la aplicación. Antes de seguir adelante, puedes revisar el archivo de migración de tu base de datos para ver la relación que ahora existe entre tus modelos y tablas de base de datos. Utiliza el siguiente comando para ver el contenido del archivo, asegurándote de sustituir el timestamp de tu propio archivo de migración por lo que se muestra aquí:

  1. cat db/migrate/20190805132506_create_posts.rb

Verás la siguiente salida:

Output
class CreatePosts < ActiveRecord::Migration[5.2] def change create_table :posts do |t| t.text :body t.references :shark, foreign_key: true t.timestamps end end end

Como puedes ver, la tabla incluye una columna para una clave foránea de tiburón. Esta clave tendrá la forma de model_name_id — en nuestro caso, shark_id.

Rails también ha establecido la relación entre los modelos en otro lugar. Echa un vistazo al modelo Post recién generado con el siguiente comando:

cat app/models/post.rb
Output
class Post < ApplicationRecord belongs_to :shark end

La asociación belongs_to establece una relación entre modelos en la que una única instancia del modelo declarado pertenece a una única instancia del modelo nombrado. En el caso de nuestra aplicación, esto significa que una sola publicación pertenece a un solo tiburón.

Además de establecer esta relación, el comando rails generate scaffold también creó rutas y vistas para las publicaciones, al igual que lo hizo para nuestros recursos de tiburón en Paso 3 de Cómo crear una aplicación Ruby on Rails.

Este es un comienzo útil, pero necesitaremos configurar algunas rutas adicionales y consolidar la asociación de Active Record para el modelo Shark para que la relación entre nuestros modelos y rutas funcione como se desea.

Paso 2 — Especificar rutas anidadas y asociaciones para el modelo padre

Para agregar la asociación has_many al modelo Shark, abre app/models/shark.rb usando nano o tu editor favorito:

Agrega la siguiente línea al archivo para establecer la relación entre tiburones y publicaciones:

  1. nano app/models/shark.rb

Algo que vale la pena considerar aquí es qué sucede con las publicaciones una vez que se elimina un tiburón en particular. Probablemente no queremos que las publicaciones asociadas con un tiburón eliminado persistan en la base de datos. Para asegurarnos de que cualquier publicación asociada con un tiburón dado se elimine cuando se elimine ese tiburón, podemos incluir la opción dependent con la asociación.

~/sharkapp/app/models/shark.rb
class Shark < ApplicationRecord
  has_many :posts
  validates :name, presence: true, uniqueness: true
  validates :facts, presence: true
end

Agrega el siguiente código al archivo para asegurar que la acción destroy en un tiburón dado elimine cualquier publicación asociada:

Una vez que hayas terminado de hacer estos cambios, guarda y cierra el archivo. Si estás usando nano, puedes hacer esto presionando CTRL+X, Y, luego ENTER.

~/sharkapp/app/models/shark.rb
class Shark < ApplicationRecord
  has_many :posts , dependent: :destroy
  validates :name, presence: true, uniqueness: true
  validates :facts, presence: true
end

A continuación, abre tu archivo config/routes.rb para modificar la relación entre tus rutas de recursos:

Actualmente, el archivo luce así:

  1. nano config/routes.rb

# Para obtener detalles sobre el DSL disponible dentro de este archivo, consulta http://guides.rubyonrails.org/routing.htmlEl código actual establece una relación independiente entre nuestras rutas, cuando lo que nos gustaría expresar es una relación dependiente entre los tiburones y sus publicaciones asociadas.

~/sharkapp/config/routes.rb
Rails.application.routes.draw do
  resources :posts 
  resources :sharks

  root 'sharks#index'
  # Para obtener más información sobre el DSL disponible en este archivo, consulte http://guides.rubyonrails.org/routing.html
end

El código actual establece una relación independiente entre nuestras rutas, cuando lo que nos gustaría expresar es una relación dependiente entre tiburones y sus publicaciones asociadas.

Actualicemos nuestra declaración de ruta para hacer que :sharks sea el padre de :posts. Actualice el código en el archivo para que se vea como el siguiente:

~/sharkapp/config/routes.rb
Rails.application.routes.draw do
  resources :sharks do
    resources :posts
end
  root 'sharks#index'
  # Para obtener más información sobre el DSL disponible en este archivo, consulte http://guides.rubyonrails.org/routing.html
end

Guarde y cierre el archivo cuando termine de editar.

Con estos cambios en su lugar, puede pasar a actualizar su controlador posts.

Paso 3: Actualizar el controlador de publicaciones

La asociación entre nuestros modelos nos da métodos que podemos usar para crear nuevas instancias de publicaciones asociadas con tiburones específicos. Para usar estos métodos, necesitaremos agregarlos a nuestro controlador de publicaciones.

Abra el archivo del controlador de publicaciones:

  1. nano app/controllers/posts_controller.rb

Actualmente, el archivo se ve así:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :set_post, only: [:show, :edit, :update, :destroy]

  # GET /publicaciones
  # GET /publicaciones.json
  def index
    @posts = Post.all
  end

  # GET /publicaciones/1
  # GET /publicaciones/1.json
  def show
  end

  # GET /publicaciones/new
  def new
    @post = Post.new
  end

  # GET /publicaciones/1/edit
  def edit
  end

  # POST /publicaciones
  # POST /publicaciones.json
  def create
    @post = Post.new(post_params)

    respond_to do |format|
      if @post.save
        format.html { redirect_to @post, notice: 'Post was successfully created.' }
        format.json { render :show, status: :created, location: @post }
      else
        format.html { render :new }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /publicaciones/1
  # PATCH/PUT /publicaciones/1.json
  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to @post, notice: 'Post was successfully updated.' }
        format.json { render :show, status: :ok, location: @post }
      else
        format.html { render :edit }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /publicaciones/1
  # DELETE /publicaciones/1.json
  def destroy
    @post.destroy
    respond_to do |format|
      format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Utilice callbacks para compartir configuración común o restricciones entre acciones.
    def set_post
      @post = Post.find(params[:id])
    end

    # Nunca confíe en los parámetros de Internet, solo permita la lista blanca a través de.
    def post_params
      params.require(:post).permit(:body, :shark_id)
    end
end

Al igual que nuestro controlador de tiburones, los métodos de este controlador funcionan con instancias de la clase de Publicación asociada. Por ejemplo, el método nuevo crea una nueva instancia de la clase Publicación, el método índice obtiene todas las instancias de la clase y el método set_publicación utiliza find y params para seleccionar una publicación en particular por su id. Sin embargo, si queremos que nuestras instancias de publicación estén asociadas con instancias de tiburones en particular, entonces necesitaremos modificar este código, ya que la clase Publicación actualmente opera como una entidad independiente.

Nuestras modificaciones harán uso de dos cosas:

  • Los métodos que se hicieron disponibles para nosotros cuando añadimos las asociaciones belongs_to y has_many a nuestros modelos. Específicamente, ahora tenemos acceso al método build gracias a la asociación has_many que definimos en nuestro modelo Shark. Este método nos permitirá crear una colección de objetos de publicaciones asociadas con un objeto tiburón particular, utilizando la clave foránea shark_id que existe en nuestra base de datos de posts.
  • Las rutas y ayudantes de enrutamiento que se hicieron disponibles cuando creamos una ruta anidada de posts. Para obtener una lista completa de ejemplos de rutas que se vuelven disponibles cuando creas relaciones anidadas entre recursos, consulta la documentación de Rails. Por ahora, será suficiente para nosotros saber que para cada tiburón específico —digamos sharks/1— habrá una ruta asociada para las publicaciones relacionadas con ese tiburón: sharks/1/posts. También habrá ayudantes de enrutamiento como shark_posts_path(@shark) y edit_sharks_posts_path(@shark) que se refieren a estas rutas anidadas.En el archivo, comenzaremos escribiendo un método, get_shark, que se ejecutará antes de cada acción en el controlador. Este método creará una variable de instancia local @shark encontrando una instancia de tiburón por shark_id. Con esta variable disponible para nosotros en el archivo, será posible relacionar publicaciones con un tiburón específico en los otros métodos.

En el archivo, comenzaremos escribiendo un método, get_shark, que se ejecutará antes de cada acción en el controlador. Este método creará una instancia local de @shark buscando una instancia de tiburón por shark_id. Con esta variable disponible en el archivo, será posible relacionar publicaciones con un tiburón específico en los otros métodos.

Encima de los otros métodos private en la parte inferior del archivo, agregue el siguiente método:

~/sharkapp/controllers/posts_controller.rb
. . . 
private
  def get_shark
@shark = Shark.find(params[:shark_id])
end
  # Utilizar callbacks para compartir configuración o restricciones comunes entre acciones.
. . . 

A continuación, agregue el filtro correspondiente a la parte superior del archivo, antes del filtro existente:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :get_shark

Esto garantizará que get_shark se ejecute antes de cada acción definida en el archivo.

A continuación, puede usar esta instancia de @shark para reescribir el método index. En lugar de obtener todas las instancias de la clase Post, queremos que este método devuelva todas las instancias de publicaciones asociadas con una instancia de tiburón específica.

Modifique el método index para que se vea así:

~/sharkapp/controllers/posts_controller.rb
. . .
  def index
    @posts = @shark.posts
  end
. . .

El método new necesitará una revisión similar, ya que queremos que una nueva instancia de publicación esté asociada con un tiburón específico. Para lograr esto, podemos usar el método build, junto con nuestra variable de instancia local @shark.

Cambie el método new para que se vea así:

~/sharkapp/controllers/posts_controller.rb
. . . 
  def new
    @post = @shark.posts.build
  end
. . . 

Este método crea un objeto de publicación que está asociado con la instancia de tiburón específica desde el método get_shark.

Actualiza el método create para que se vea así:

A continuación, echa un vistazo al método update. Este método utiliza una variable de instancia @post, la cual no está establecida explícitamente en el propio método. ¿De dónde proviene esta variable?

~/sharkapp/controllers/posts_controller.rb
  def create
    @post = @shark.posts.build(post_params)

        respond_to do |format|
         if @post.save  
            format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully created.' }
            format.json { render :show, status: :created, location: @post }
         else
            format.html { render :new }
            format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

Echa un vistazo a los filtros en la parte superior del archivo. El segundo filtro before_action auto-generado proporciona una respuesta:

El método update (al igual que show, edit, y destroy) toma una variable @post del método set_post. Ese método, listado bajo el método get_shark con nuestros otros métodos private, actualmente se ve así:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :get_shark
  before_action :set_post, only: [:show, :edit, :update, :destroy]
  . . .

Siguiendo con los métodos que hemos utilizado en otras partes del archivo, necesitaremos modificar este método para que @post se refiera a una instancia particular en la colección de publicaciones asociadas con un tiburón específico. Ten en cuenta el método build aquí: gracias a las asociaciones entre nuestros modelos y los métodos (como build) que están disponibles para nosotros gracias a esas asociaciones, cada una de nuestras instancias de publicaciones es parte de una colección de objetos asociados con un tiburón específico. Por lo tanto, tiene sentido que al consultar una publicación en particular, consultemos la colección de publicaciones asociadas con un tiburón específico.Actualiza set_post para que se vea así:En lugar de encontrar una instancia particular de toda la clase Post por id, en su lugar buscamos un id coincidente en la colección de publicaciones asociadas con un tiburón específico.

~/sharkapp/controllers/posts_controller.rb
. . . 
private
. . . 
  def set_post
    @post = Post.find(params[:id])
  end
. . .

Manteniendo los métodos que hemos utilizado en otros lugares del archivo, necesitamos modificar este método para que @post se refiera a una instancia específica en la colección de publicaciones asociadas con un tiburón en particular. Ten en cuenta el método build aquí: gracias a las asociaciones entre nuestros modelos y los métodos (como build) disponibles para nosotros debido a esas asociaciones, cada instancia de nuestra publicación es parte de una colección de objetos asociados con un tiburón específico. Por lo tanto, tiene sentido que al consultar una publicación en particular, busquemos la colección de publicaciones asociadas con un tiburón específico.

Actualiza set_post para que se vea así:

~/sharkapp/controllers/posts_controller.rb
. . . 
private
. . . 
  def set_post
    @post = @shark.posts.find(params[:id])
  end
. . .

En lugar de encontrar una instancia específica de toda la clase Post por id, buscamos un id coincidente en la colección de publicaciones asociadas con un tiburón específico.

Con ese método actualizado, podemos ver los métodos update y destroy.

El método update hace uso de la variable de instancia @post de set_post y la usa con los post_params que el usuario ha ingresado en el formulario edit. En caso de éxito, queremos que Rails envíe al usuario de vuelta a la vista index de las publicaciones asociadas con un tiburón específico. En caso de errores, Rails volverá a renderizar la plantilla edit.

En este caso, el único cambio que necesitaremos hacer es en la declaración redirect_to, para manejar las actualizaciones exitosas. Actualícelo para redireccionar a shark_post_path(@shark), lo que redireccionará a la vista index de las publicaciones del tiburón seleccionado:

~/sharkapp/controllers/posts_controller.rb
. . . 
  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to shark_post_path(@shark), notice: 'Post was successfully updated.' }
        format.json { render :show, status: :ok, location: @post }
      else
        format.html { render :edit }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end
. . .

A continuación, haremos un cambio similar en el método destroy. Actualice el método redirect_to para redireccionar las solicitudes a shark_posts_path(@shark) en caso de éxito:

~/sharkapp/controllers/posts_controller.rb
. . . 
  def destroy
    @post.destroy
     respond_to do |format|
      format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully destroyed.' }
      format.json { head :no_content }
    end
  end
. . .

Este es el último cambio que haremos. Ahora tiene un archivo de controlador de publicaciones que se ve así:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :get_shark
  before_action :set_post, only: [:show, :edit, :update, :destroy]

  # GET /posts
  # GET /posts.json
  def index
    @posts = @shark.posts
  end

  # GET /posts/1
  # GET /posts/1.json
  def show
  end

  # GET /posts/new
  def new
    @post = @shark.posts.build
  end

  # GET /posts/1/edit
  def edit
  end

  # POST /posts
  # POST /posts.json
  def create
    @post = @shark.posts.build(post_params)

        respond_to do |format|
         if @post.save  
            format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully created.' }
            format.json { render :show, status: :created, location: @post }
         else
            format.html { render :new }
            format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /posts/1
  # PATCH/PUT /posts/1.json
  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to shark_post_path(@shark), notice: 'Post was successfully updated.' }
        format.json { render :show, status: :ok, location: @post }
      else
        format.html { render :edit }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /posts/1
  # DELETE /posts/1.json
  def destroy
    @post.destroy
    respond_to do |format|
      format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private

   def get_shark
     @shark = Shark.find(params[:shark_id])
   end
    # Utilice devoluciones de llamada para compartir configuración o restricciones comunes entre acciones.
    def set_post
      @post = @shark.posts.find(params[:id])
    end

    # Nunca confíe en los parámetros de internet aterradora, solo permita la lista blanca.
    def post_params
      params.require(:post).permit(:body, :shark_id)
    end
end

El controlador gestiona cómo se pasa la información de las plantillas de vista a la base de datos y viceversa. Nuestro controlador ahora refleja la relación entre nuestros modelos Shark y Post, en la que las publicaciones están asociadas con tiburones particulares. Podemos pasar a modificar las plantillas de vista en sí, que son donde los usuarios pasarán y modificarán la información de las publicaciones sobre tiburones particulares.

Paso 4 — Modificando Vistas

Nuestras revisiones de la plantilla de vista implicarán cambiar las plantillas que se relacionan con las publicaciones y también modificar nuestra vista show de tiburones, ya que queremos que los usuarios vean las publicaciones asociadas con tiburones específicos.

Comencemos con la plantilla fundamental para nuestras publicaciones: el parcial form que se reutiliza en múltiples plantillas de publicaciones. Abre ese formulario ahora:

  1. nano app/views/posts/_form.html.erb

En lugar de pasar solo el modelo post al ayudante de formulario form_with, pasaremos tanto el modelo shark como el modelo post, con post establecido como recurso secundario.

Cambia la primera línea del archivo para que se vea así, reflejando la relación entre nuestros recursos tiburón y publicación:

~/sharkapp/views/posts/_form.html.erb
<%= form_with(model: [@shark, post], local: true) do |form| %>
. . . 

A continuación, elimina la sección que enumera el shark_id del tiburón relacionado, ya que esta no es información esencial en la vista.

El formulario terminado, completo con nuestras ediciones en la primera línea y sin la sección eliminada de shark_id, se verá así:

~/sharkapp/views/posts/_form.html.erb
<%= form_with(model: [@shark, post], local: true) do |form| %>
  <% if post.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h2>

      <ul>
      <% post.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.label :body %>
    <%= form.text_area :body %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

Guarda y cierra el archivo cuando termines de editar.

A continuación, abre la vista index, que mostrará las publicaciones asociadas con un tiburón en particular:

  1. nano app/views/posts/index.html.erb

A continuación, cambie la redirección de Show para dirigir a los usuarios a la vista show del tiburón asociado, ya que es probable que quieran una forma de volver al tiburón original. Podemos hacer uso de la variable de instancia @shark que establecimos en el controlador aquí, ya que Rails hace que las variables de instancia creadas en el controlador estén disponibles para todas las vistas. También cambiaremos el texto del enlace de Show a Show Shark, para que los usuarios comprendan mejor su función.

Actualice esta línea a lo siguiente:

En la siguiente línea, queremos asegurarnos de que los usuarios sean dirigidos al camino anidado correcto cuando vayan a editar una publicación. Esto significa que en lugar de ser dirigidos a posts/post_id/edit, los usuarios serán dirigidos a sharks/shark_id/posts/post_id/edit. Para hacer esto, usaremos el ayudante de enrutamiento shark_post_path y nuestros modelos, que Rails tratará como URLs. También actualizaremos el texto del enlace para que su función sea más clara.

Actualice la línea de Edit para que se vea así:

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
. . . 

A continuación, agreguemos un cambio similar al enlace Destroy, actualizando su función en la cadena y agregando nuestros recursos shark y post:

Finalmente, en el fondo del formulario, querremos actualizar el camino de New Post para llevar a los usuarios al camino anidado apropiado cuando quieran crear una nueva publicación. Actualice la última línea del archivo para hacer uso del ayudante de enrutamiento new_shark_post_path(@shark):El archivo terminado se verá así:

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>

Guarda y cierra el archivo cuando hayas terminado de editar.

Las otras ediciones que haremos en las vistas de publicaciones no serán tan numerosas, ya que nuestras otras vistas usan el parcial form que ya hemos editado. Sin embargo, querremos actualizar las referencias de link_to en las otras plantillas de publicaciones para reflejar los cambios que hemos realizado en nuestro parcial form.

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>
        <td><%= link_to 'Edit Post', edit_shark_post_path(@shark, post) %></td>

Abre app/views/posts/new.html.erb:

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>
        <td><%= link_to 'Edit Post', edit_shark_post_path(@shark, post) %></td>
        <td><%= link_to 'Destroy Post', [@shark, post], method: :delete, data: { confirm: 'Are you sure?' } %></td>

Actualiza la referencia de link_to en la parte inferior del archivo para hacer uso del ayudante shark_posts_path(@shark):

~/sharkapp/app/views/posts/index.html.erb
. . . 
<%= link_to 'New Post', new_shark_post_path(@shark) %>

Guarda y cierra el archivo cuando hayas terminado de realizar este cambio.

~/sharkapp/app/views/posts/index.html.erb
<p id="notice"><%= notice %></p>

<h1>Posts</h1>

<table>
  <thead>
    <tr>
      <th>Body</th>
      <th>Shark</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>
        <td><%= link_to 'Edit Post', edit_shark_post_path(@shark, post) %></td>
        <td><%= link_to 'Destroy Post', [@shark, post], method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Post', new_shark_post_path(@shark) %>

A continuación, abre la plantilla edit:

Además de la ruta Back, actualizaremos Show para reflejar nuestros recursos anidados. Cambia las dos últimas líneas del archivo para que se vean así:

Guarda y cierra el archivo.

  1. nano app/views/posts/new.html.erb

A continuación, abre la plantilla show:Realiza las siguientes ediciones en las rutas Edit y Back en la parte inferior del archivo:Guarda y cierra el archivo cuando hayas terminado.Como paso final, querremos actualizar la vista show para nuestros tiburones para que las publicaciones sean visibles para tiburones individuales. Abre ese archivo ahora:Nuestras ediciones aquí incluirán agregar una sección Posts al formulario y un enlace Add Post en la parte inferior del archivo.

~/sharkapp/app/views/posts/new.html.erb
. . . 
<%= link_to 'Back', shark_posts_path(@shark) %>

Guarda y cierra el archivo cuando hayas terminado de hacer este cambio.

A continuación, abre la plantilla edit:

  1. nano app/views/posts/edit.html.erb

Además del camino Back, actualizaremos Show para reflejar nuestros recursos anidados. Cambia las dos últimas líneas del archivo para que se vean así:

~/sharkapp/app/views/posts/edit.html.erb
. . . 
<%= link_to 'Show', [@shark, @post] %> |
<%= link_to 'Back', shark_posts_path(@shark) %>

Guarda y cierra el archivo.

A continuación, abre la plantilla show:

nano app/views/posts/show.html.erb

Realiza los siguientes cambios en los caminos Edit y Back al final del archivo:

~/sharkapp/app/views/posts/edit.html.erb
. . .
<%= link_to 'Edit', edit_shark_post_path(@shark, @post) %> |
<%= link_to 'Back', shark_posts_path(@shark) %>

Guarda y cierra el archivo cuando hayas terminado.

Como paso final, querríamos actualizar la vista show para nuestras tiburones para que las publicaciones sean visibles para tiburones individuales. Abre ese archivo ahora:

  1. nano app/views/sharks/show.html.erb

Nuestros cambios aquí incluirán agregar una sección Posts al formulario y un enlace Add Post al final del archivo.

Debajo de los Facts de un tiburón determinado, agregaremos una nueva sección que itere a través de cada instancia en la colección de publicaciones asociadas con este tiburón, mostrando el body de cada publicación.

Agrega el siguiente código debajo de la sección Facts del formulario, y por encima de las redirecciones al final del archivo:

~/sharkapp/app/views/sharks/show.html.erb
. . .
<p>
  <strong>Facts:</strong>
  <%= @shark.facts %>
</p>

<h2>Posts</h2>
<% for post in @shark.posts %>
    <ul>
      <li><%= post.body %></li>
  </ul>
<% end %>

<%= link_to 'Edit', edit_shark_path(@shark) %> |
. . . 

A continuación, agrega una nueva redirección para permitir a los usuarios agregar una nueva publicación para este tiburón en particular:

~/sharkapp/app/views/sharks/show.html.erb
. . .
<%= link_to 'Edit', edit_shark_path(@shark) %> |
<%= link_to 'Add Post', shark_posts_path(@shark) %> |
<%= link_to 'Back', sharks_path %>

Guarda y cierra el archivo cuando hayas terminado de editar.

Has realizado cambios en los modelos, controladores y vistas de su aplicación para asegurar que las publicaciones siempre estén asociadas con un tiburón en particular. Como último paso, podemos agregar algunas validaciones a nuestro modelo Post para garantizar la consistencia en los datos que se guardan en la base de datos.

Paso 5: Agregar validaciones y probar la aplicación

En Paso 5 de Cómo crear una aplicación Ruby on Rails, agregaste validaciones a tu modelo Shark para asegurar uniformidad y consistencia en los datos que se guardan en la base de datos sharks. Ahora tomaremos un paso similar para garantizar garantías para la base de datos posts también.

Abre el archivo donde está definido tu modelo Post:

  1. nano app/models/post.rb

Aquí, queremos asegurarnos de que las publicaciones no estén en blanco y de que no dupliquen contenido que otros usuarios puedan haber publicado. Para lograr esto, agrega la siguiente línea al archivo:

~/sharkapp/app/models/post.rb
class Post < ApplicationRecord
  belongs_to :shark
  validates :body, presence: true, uniqueness: true
end

Guarda y cierra el archivo cuando termines de editar.

Con este último cambio en su lugar, estás listo para ejecutar tus migraciones y probar la aplicación.

Primero, ejecute sus migraciones:

  1. rails db:migrate

Luego, inicie su servidor. Si está trabajando localmente, puede hacerlo ejecutando:

  1. rails s

Si está trabajando en un servidor de desarrollo, ejecute el siguiente comando en su lugar:

  1. rails s --binding=your_server_ip

Navegue hasta la raíz de su aplicación en http://localhost:3000 o http://su_ip_del_servidor:3000.

El tutorial previo de proyecto Rails le guió a través de la adición y edición de una entrada de tiburón Blanco Grande. Si no ha agregado ningún tiburón adicional, la página de inicio de la aplicación se verá así:

Haga clic en Mostrar junto al nombre del Blanco Grande. Esto lo llevará a la vista mostrar para este tiburón. Verá el nombre del tiburón y sus datos, y un encabezado Publicaciones sin contenido. Agreguemos una publicación para poblar esta parte del formulario.

Haga clic en Agregar Publicación debajo del encabezado Publicaciones. Esto lo llevará a la vista índice de publicaciones, donde tendrá la oportunidad de seleccionar Nueva Publicación:

Gracias a los mecanismos de autenticación que implementó en Paso 6 de Cómo construir una aplicación Ruby on Rails, es posible que se le solicite autenticarse con el nombre de usuario y contraseña que creó en ese Paso, dependiendo de si ha creado una nueva sesión o no.

Haz clic en Nuevo Post, lo que te llevará a tu plantilla de publicación nueva:

En el campo Cuerpo, escribe, “¡Estos tiburones dan miedo!”

Haz clic en Crear Publicación. Serás redireccionado a la vista índice de todas las publicaciones que pertenecen a este tiburón:

Con nuestros recursos de publicación funcionando, ahora podemos probar nuestras validaciones de datos para asegurarnos de que solo se guarde la información deseada en la base de datos.

Desde la vista índice, haz clic en Nuevo Post. En el campo Cuerpo del nuevo formulario, intenta ingresar “¡Estos tiburones dan miedo!” nuevamente:

Haz clic en Crear Publicación. Verás el siguiente error:

Haz clic en Volver para regresar a la página principal de publicaciones.

Para probar nuestra otra validación, haz clic en Nuevo Post nuevamente. Deja la publicación en blanco y haz clic en Crear Publicación. Verás el siguiente error:

Con tus recursos anidados y validaciones funcionando correctamente, ahora tienes una aplicación Rails funcional que puedes usar como punto de partida para un desarrollo adicional.

Conclusión

Con su aplicación Rails en su lugar, ahora puede trabajar en cosas como el estilo y el desarrollo de otros componentes de front-end. Si desea obtener más información sobre enrutamiento y recursos anidados, la documentación de Rails es un excelente lugar para comenzar.

Para obtener más información sobre la integración de frameworks de front-end con su aplicación, eche un vistazo a Cómo configurar un proyecto Ruby on Rails con un front-end de React.

Source:
https://www.digitalocean.com/community/tutorials/how-to-create-nested-resources-for-a-ruby-on-rails-application