Введение
Ruby on Rails – это веб-фреймворк, написанный на Ruby, который предлагает разработчикам мнение по поводу разработки приложений. Работа с Rails дает разработчикам:
- Конвенции для обработки вещей, таких как маршрутизация, состояние данных и управление ресурсами.
- 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.
По мере усложнения ваших приложений Rails, скорее всего, вам придется работать с несколькими моделями, которые представляют бизнес-логику вашего приложения и взаимодействуют с вашей базой данных. Добавление связанных моделей означает установление значимых отношений между ними, что затем влияет на то, как информация передается через контроллеры вашего приложения, и на то, как она захватывается и представляется пользователям через представления.
В этом руководстве вы будете работать с существующим приложением Rails, которое предлагает пользователям факты о акулах. В этом приложении уже есть модель для обработки данных об акулах, но вы добавите вложенный ресурс для постов о отдельных акулах. Это позволит пользователям создавать более широкий объем мыслей и мнений об отдельных акулах.
Предварительные требования
Чтобы следовать этому руководству, вам понадобятся:
- 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 и npm установлены на вашем локальном компьютере или сервере разработки. В этом руководстве используются Node.js версии 10.16.3 и npm версии 6.9.0. Для инструкций по установке Node.js и npm в Ubuntu 18.04 следуйте инструкциям в разделе “Установка с использованием PPA” в Как установить Node.js на Ubuntu 18.04.
- Ruby, rbenv и Rails установлены на вашем локальном компьютере или сервере разработки, следуя шагам 1-4 в Как установить Ruby on Rails с помощью rbenv на Ubuntu 18.04. В этом руководстве используются Ruby 2.5.1, rbenv 1.1.2 и Rails 5.2.3.
- Установлен SQLite, и создано базовое приложение с информацией о акулах, следуя указаниям в Как создать приложение на Ruby on Rails.
Шаг 1 — Создание вложенной модели
Наше приложение будет использовать ассоциации Active Record для создания связи между моделями Shark
и Post
: записи будут принадлежать определенным акулам, и каждая акула может иметь несколько записей. Наши модели Shark
и Post
будут связаны через ассоциации belongs_to
и has_many
.
Первым шагом к созданию приложения таким образом будет создание модели Post
и связанных ресурсов. Для этого мы можем использовать команду rails generate scaffold
, которая предоставит нам модель, миграцию базы данных для изменения схемы базы данных, контроллер, полный набор представлений для управления стандартными операциями Создание, Чтение, Обновление и Удаление (CRUD), а также шаблоны для частичных представлений, помощников и тестов. Нам нужно будет модифицировать эти ресурсы, но использование команды scaffold
позволит нам сэкономить время и энергию, так как она генерирует структуру, которую мы можем использовать в качестве отправной точки.
Сначала убедитесь, что вы находитесь в каталоге sharkapp
для проекта Rails, который вы создали в предварительных условиях:Post
с помощью следующей команды:
Создайте свои ресурсы Post
с помощью следующей команды:
rails generate scaffold Post body:text shark:references
С помощью body:text
мы сообщаем Rails о включении поля body
в таблицу posts
базы данных — таблицу, которая сопоставляется с моделью Post
. Мы также включаем ключевое слово :references
, которое устанавливает связь между моделями Shark
и Post
. В частности, это обеспечит добавление внешнего ключа, представляющего каждую запись акулы в базе данных sharks
, в базу данных posts
.
После выполнения команды вы увидите вывод, подтверждающий ресурсы, которые Rails сгенерировал для приложения. Прежде чем двигаться дальше, вы можете проверить файл миграции базы данных, чтобы посмотреть на связь, которая теперь существует между вашими моделями и таблицами базы данных. Используйте следующую команду, чтобы посмотреть содержимое файла, заменяя временную метку на свою миграцию на то, что показано здесь:
Вы увидите следующий вывод:
Outputclass 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
Как видите, в таблице есть столбец для внешнего ключа акулы. Этот ключ будет иметь форму model_name_id
— в нашем случае, shark_id
.
Rails установил связь между моделями и в других местах. Посмотрите на недавно сгенерированную модель Post
с помощью следующей команды:
cat app/models/post.rb
Outputclass Post < ApplicationRecord
belongs_to :shark
end
belongs_to
ассоциация устанавливает связь между моделями, в которой одиночная инстанция объявляющей модели принадлежит одиночной инстанции названной модели. В случае нашего приложения это означает, что одна публикация принадлежит одной акуле.
Помимо установления этой связи, команда rails generate scaffold
также создала маршруты и представления для публикаций, как это было сделано для наших ресурсов акул в Шаге 3 из Как построить приложение Ruby on Rails.
Это хорошее начало, но нам нужно будет настроить дополнительные маршруты и упрочить ассоциацию Active Record для модели Shark
, чтобы связь между нашими моделями и маршрутами работала так, как мы хотим.
Шаг 2 — Уточнение вложенных маршрутов и ассоциаций для родительской модели
Чтобы добавить ассоциацию has_many
к модели Shark
, откройте файл app/models/shark.rb
с помощью nano
или вашего любимого редактора:
Добавьте следующую строку в файл, чтобы установить связь между акулами и постами:
Одна вещь, над которой стоит подумать здесь, – это то, что происходит с постами после удаления определенной акулы. Вероятно, мы не хотим, чтобы посты, связанные с удаленной акулой, сохранялись в базе данных. Чтобы гарантировать удаление всех постов, связанных с определенной акулой, при удалении этой акулы, мы можем использовать опцию dependent
с ассоциацией.
class Shark < ApplicationRecord
has_many :posts
validates :name, presence: true, uniqueness: true
validates :facts, presence: true
end
Добавьте следующий код в файл, чтобы гарантировать, что действие destroy
на определенной акуле удаляет все связанные посты:
После того как вы закончите внесение этих изменений, сохраните и закройте файл. Если вы используете nano
, вы можете сделать это, нажав CTRL+X
, затем Y
, и нажав ENTER
.
class Shark < ApplicationRecord
has_many :posts , dependent: :destroy
validates :name, presence: true, uniqueness: true
validates :facts, presence: true
end
Затем откройте ваш файл config/routes.rb
, чтобы изменить отношение между вашими маршрутами:
В настоящее время файл выглядит так:
# Для получения дополнительной информации о DSL, доступном в этом файле, см. http://guides.rubyonrails.org/routing.html
Rails.application.routes.draw do
resources :posts
resources :sharks
root 'sharks#index'
# Подробности о DSL, доступной в этом файле, см. на http://guides.rubyonrails.org/routing.html
end
Текущий код устанавливает независимую связь между нашими маршрутами, когда нам хотелось бы выразить зависимую связь между акулами и их связанными постами.
Давайте обновим объявление маршрута, чтобы сделать :sharks
родителем :posts
. Обновите код в файле, чтобы он выглядел следующим образом:
Rails.application.routes.draw do
resources :sharks do
resources :posts
end
root 'sharks#index'
# Подробности о DSL, доступной в этом файле, см. на http://guides.rubyonrails.org/routing.html
end
Сохраните и закройте файл после завершения редактирования.
С внесенными изменениями вы можете перейти к обновлению вашего контроллера posts
.
Шаг 3 — Обновление Контроллера Постов
Соединение между нашими моделями дает нам методы, которые мы можем использовать для создания новых экземпляров постов, связанных с определенными акулами. Чтобы использовать эти методы, нам нужно добавить их в наш контроллер постов.
Откройте файл контроллера постов:
В настоящее время файл выглядит следующим образом:
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
# GET /posts
# GET /posts.json
def index
@posts = Post.all
end
# GET /posts/1
# GET /posts/1.json
def show
end
# GET /posts/new
def new
@post = Post.new
end
# GET /posts/1/edit
def edit
end
# POST /posts
# POST /posts.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 /posts/1
# PATCH/PUT /posts/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 /posts/1
# DELETE /posts/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
# Используйте обратные вызовы для обмена общим настройками или ограничениями между действиями.
def set_post
@post = Post.find(params[:id])
end
# Никогда не доверяйте параметрам из страшного интернета, разрешайте только белый список через.
def post_params
params.require(:post).permit(:body, :shark_id)
end
end
Как и наш контроллер для акул, методы этого контроллера работают с экземплярами связанного класса Post
. Например, метод new
создает новый экземпляр класса Post
, метод index
получает все экземпляры класса, а метод set_post
использует find
и params
для выбора конкретного поста по id
. Однако, если мы хотим, чтобы наши экземпляры постов были связаны с определенными экземплярами акул, тогда нам нужно будет изменить этот код, так как класс Post
в настоящее время действует как независимая сущность.
Наши модификации будут использовать две вещи:
- Методы, которые стали доступны нам, когда мы добавили ассоциации
belongs_to
иhas_many
к нашим моделям. Конкретно, теперь у нас есть доступ к методуbuild
, благодаря ассоциацииhas_many
, которую мы определили в нашей моделиShark
. Этот метод позволит нам создавать коллекцию объектов постов, связанных с определенным объектом акулы, используя внешний ключshark_id
, который существует в нашей базе данныхposts
. - Маршруты и вспомогательные функции маршрутизации, которые стали доступны при создании вложенного маршрута
posts
. Для полного списка примеров маршрутов, которые становятся доступны при создании вложенных отношений между ресурсами, см. документацию Rails. Пока будет достаточно знать, что для каждой конкретной акулы — скажем,sharks/1
— будет связанный маршрут для постов, связанных с этой акулой:sharks/1/posts
. Также будут вспомогательные функции маршрутизации, такие какshark_posts_path(@shark)
иedit_sharks_posts_path(@shark)
, которые относятся к этим вложенным маршрутам.В файле мы начнем с написания метода get_shark
, который будет выполняться перед каждым действием в контроллере. Этот метод будет создавать локальную переменную экземпляра@shark
, найдя экземпляр акулы поshark_id
. С этой переменной, доступной нам в файле, будет возможно связывать посты с определенной акулой в других методах.
В файле мы начнем с написания метода get_shark
, который будет выполняться перед каждым действием в контроллере. Этот метод создаст локальную переменную экземпляра @shark
, находящую экземпляр акулы по shark_id
. С этой переменной, доступной нам в файле, станет возможным связать посты с конкретным экземпляром акулы в других методах.
Выше других private
методов в нижней части файла добавьте следующий метод:
. . .
private
def get_shark
@shark = Shark.find(params[:shark_id])
end
# Используйте обратные вызовы для совместного использования общих настроек или ограничений между действиями.
. . .
Далее добавьте соответствующий фильтр в начало файла, перед существующим фильтром:
class PostsController < ApplicationController
before_action :get_shark
Это обеспечит выполнение get_shark
перед каждым определенным действием в файле.
Далее вы можете использовать этот экземпляр @shark
для переписывания метода index
. Вместо того, чтобы захватывать все экземпляры класса Post
, мы хотим, чтобы этот метод возвращал все экземпляры постов, связанные с конкретным экземпляром акулы.
Измените метод index
так, чтобы он выглядел следующим образом:
. . .
def index
@posts = @shark.posts
end
. . .
Метод new
также потребует аналогичной корректировки, поскольку мы хотим, чтобы новый экземпляр поста был связан с конкретным экземпляром акулы. Для достижения этого, мы можем использовать метод build
, вместе с нашей локальной переменной экземпляра @shark
.
Измените метод new
так, чтобы он выглядел следующим образом:
. . .
def new
@post = @shark.posts.build
end
. . .
Этот метод создает объект поста, связанный с конкретным экземпляром акулы из метода get_shark
.
Обновите метод create
следующим образом:
Затем взгляните на метод update
. Этот метод использует переменную экземпляра @post
, которая не явно устанавливается в самом методе. Откуда берется эта переменная?
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
Взгляните на фильтры в верхней части файла. Второй автоматически сгенерированный фильтр before_action
дает ответ:
Метод update
(как и show
, edit
и destroy
) получает переменную @post
из метода set_post
. Этот метод, перечисленный под методом get_shark
с нашими другими private
методами, в настоящее время выглядит следующим образом:
class PostsController < ApplicationController
before_action :get_shark
before_action :set_post, only: [:show, :edit, :update, :destroy]
. . .
Соответствуя методам, используемым в других частях файла, нам нужно изменить этот метод так, чтобы @post
ссылался на конкретный экземпляр в build
– благодаря связям между нашими моделями и методам (таким как build
), которые доступны нам благодаря этим связям, каждый из наших экземпляров поста является частью коллекции объектов, связанных с определенной акулой. Поэтому логично, что при запросе конкретного поста мы будем запрашивать коллекцию постов, связанных с определенной акулой.set_post
, чтобы он выглядел следующим образом:Post
по id
, мы вместо этого ищем соответствующий id
в коллекции постов, связанных с определенной акулой.
. . .
private
. . .
def set_post
@post = Post.find(params[:id])
end
. . .
Соответственно методам, которые мы использовали в других частях файла, нам нужно изменить этот метод так, чтобы @post
относился к конкретному экземпляру в коллекции постов, связанных с определенным акулой. Держите в уме метод build
– благодаря ассоциациям между нашими моделями и методам (как build
), доступными для нас благодаря этим ассоциациям, каждый наш экземпляр поста является частью коллекции объектов, связанных с определенной акулой. Поэтому логично, что при запросе определенного поста, мы запрашиваем коллекцию постов, связанных с определенной акулой.
Обновите set_post
следующим образом:
. . .
private
. . .
def set_post
@post = @shark.posts.find(params[:id])
end
. . .
Вместо поиска определенного экземпляра всего класса Post
по id
, мы ищем соответствие id
в коллекции постов, связанных с определенной акулой.
Обновив этот метод, мы можем рассмотреть методы update
и destroy
.
Метод update
использует переменную экземпляра @post
из set_post
и использует ее с post_params
, которые пользователь ввел в форме edit
. В случае успеха мы хотим, чтобы Rails перенаправил пользователя к представлению index
постов, связанных с определенной акулой. В случае ошибок Rails снова отрендерит шаблон edit
.
В этом случае единственное изменение, которое нам нужно сделать, – это утверждение redirect_to
, чтобы обрабатывать успешные обновления. Обновите его, чтобы перенаправить на shark_post_path(@shark)
, что перенаправит на представление index
постов выбранной акулы:
. . .
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
. . .
Далее мы сделаем аналогичное изменение в методе destroy
. Обновите метод redirect_to
, чтобы перенаправлять запросы на shark_posts_path(@shark)
в случае успеха:
. . .
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
. . .
Это последнее изменение, которое мы сделаем. Теперь у вас есть файл контроллера постов, который выглядит следующим образом:
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
# Использовать обратные вызовы для совместного использования общих установок или ограничений между действиями.
def set_post
@post = @shark.posts.find(params[:id])
end
# Никогда не доверяйте параметрам от пугающего интернета, разрешайте только белый список.
def post_params
params.require(:post).permit(:body, :shark_id)
end
end
Контроллер управляет тем, как информация передается из шаблонов представлений в базу данных и наоборот. Наш контроллер теперь отражает взаимосвязь между нашими моделями Shark
и Post
, в которой посты связаны с определенными акулами. Мы можем перейти к изменению самих шаблонов представлений, где пользователи будут вводить и изменять информацию о постах о конкретных акулах.
Шаг 4 — Изменение представлений
Наши изменения шаблонов представлений будут затрагивать шаблоны, связанные с постами, а также изменение представления show
для акул, поскольку мы хотим, чтобы пользователи видели посты, связанные с конкретными акулами.
Начнем с основного шаблона для наших постов: частичного form
, который используется в нескольких шаблонах постов. Откройте этот форм теперь:
Вместо передачи только модели post
в помощник формы form_with
, мы будем передавать как модель shark
, так и модель post
, при этом post
устанавливается как дочерний ресурс.
Измените первую строку файла, чтобы она отражала отношение между нашими ресурсами акулы и поста:
<%= form_with(model: [@shark, post], local: true) do |form| %>
. . .
Далее, удалите раздел, в котором перечисляется shark_id
связанной акулы, поскольку это не является существенной информацией в представлении.
Законченный форм, включая наши правки в первой строке и без удаленного раздела shark_id
, будет выглядеть следующим образом:
<%= 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 %>
Сохраните и закройте файл после завершения редактирования.
Далее откройте представление index
, которое покажет посты, связанные с определенной акулой:
Затем измените перенаправление Show
, чтобы направлять пользователей на вид show
для соответствующей акулы, поскольку они скорее всего захотят иметь способ вернуться к исходной акуле. Мы можем воспользоваться переменной экземпляра @shark
, которую мы установили здесь в контроллере, поскольку Rails делает переменные экземпляра, созданные в контроллере, доступными для всех видов. Мы также изменим текст ссылки с Show
на Show Shark
, чтобы пользователи лучше поняли ее функцию.
Обновите эту строку следующим образом:
На следующей строке мы хотим убедиться, что пользователи направляются по правильному вложенному пути, когда они переходят к редактированию сообщения. Это означает, что вместо перехода на posts/post_id/edit
пользователи будут направляться на sharks/shark_id/posts/post_id/edit
. Для этого мы будем использовать вспомогательный маршрут shark_post_path
и наши модели, которые Rails будет рассматривать как URL-адреса. Мы также обновим текст ссылки, чтобы сделать ее функцию более понятной.
Обновите строку Edit
, чтобы она выглядела следующим образом:
. . .
<tbody>
<% @posts.each do |post| %>
<tr>
<td><%= post.body %></td>
<td><%= post.shark.name %></td>
. . .
Затем добавим аналогичное изменение к ссылке Destroy
, обновив ее функцию в строке и добавив наши ресурсы shark
и post
:
Наконец, в конце формы мы захотим обновить путь New Post
, чтобы пользователи переходили по соответствующему вложенному пути, когда захотят создать новый пост. Обновите последнюю строку файла, чтобы использовать вспомогательный маршрут new_shark_post_path(@shark)
:
. . .
<tbody>
<% @posts.each do |post| %>
<tr>
<td><%= post.body %></td>
<td><%= post.shark.name %></td>
<td><%= link_to 'Show Shark', [@shark] %></td>
Сохраните и закройте файл после завершения редактирования.
Другие изменения, которые мы внесем в представления постов, не будут такими обширными, поскольку наши другие представления используют частичное представление form
, которое мы уже отредактировали. Однако мы захотим обновить ссылки link_to
в других шаблонах постов, чтобы отразить изменения, которые мы внесли в наше частичное представление form
.
. . .
<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>
Откройте app/views/posts/new.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>
Обновите ссылку link_to
внизу файла, чтобы использовать помощник shark_posts_path(@shark)
:
. . .
<%= link_to 'New Post', new_shark_post_path(@shark) %>
Сохраните и закройте файл после завершения этого изменения.
<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) %>
Затем откройте шаблон edit
:
Помимо пути Back
, мы обновим Show
, чтобы отразить наши вложенные ресурсы. Измените последние две строки файла так:
Сохраните и закройте файл.
Затем откройте шаблон show
:Edit
и Back
внизу файла:show
для наших акул, чтобы посты были видны для отдельных акул. Откройте этот файл сейчас:Posts
в форму и ссылки Add Post
внизу файла.
. . .
<%= link_to 'Back', shark_posts_path(@shark) %>
Сохраните и закройте файл после завершения внесения этого изменения.
Далее, откройте шаблон edit
:
Помимо пути Back
, мы обновим Show
для отражения наших вложенных ресурсов. Измените последние две строки файла, чтобы они выглядели следующим образом:
. . .
<%= link_to 'Show', [@shark, @post] %> |
<%= link_to 'Back', shark_posts_path(@shark) %>
Сохраните и закройте файл.
Далее, откройте шаблон show
:
nano app/views/posts/show.html.erb
Внесите следующие правки в пути Edit
и Back
в нижней части файла:
. . .
<%= link_to 'Edit', edit_shark_post_path(@shark, @post) %> |
<%= link_to 'Back', shark_posts_path(@shark) %>
Сохраните и закройте файл после завершения.
В качестве последнего шага, нам нужно обновить представление show
для наших акул, чтобы сообщения были видны для отдельных акул. Откройте этот файл сейчас:
Наши правки здесь будут включать добавление раздела Posts
в форму и ссылку Add Post
в нижней части файла.
Под разделом Facts
для данной акулы, мы добавим новый раздел, который проходит через каждую запись в коллекции сообщений, связанных с этой акулой, выводя тело каждого сообщения.
Добавьте следующий код под разделом Facts
формы и над перенаправлениями в нижней части файла:
. . .
<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) %> |
. . .
Далее, добавьте новое перенаправление, чтобы позволить пользователям добавить новое сообщение для этой конкретной акулы:
. . .
<%= link_to 'Edit', edit_shark_path(@shark) %> |
<%= link_to 'Add Post', shark_posts_path(@shark) %> |
<%= link_to 'Back', sharks_path %>
Сохраните и закройте файл после завершения правки.
Теперь вы внести изменения в модели, контроллеры и представления вашего приложения, чтобы гарантировать, что посты всегда связаны с определенным акулой. В качестве последнего шага мы можем добавить некоторые проверки в нашу модель Post
, чтобы обеспечить согласованность данных, сохраняемых в базе данных.
Шаг 5 – Добавление проверок и тестирование приложения
В Шаге 5 из Как создать приложение Ruby on Rails, вы добавили проверки в модель Shark
для обеспечения единообразия и согласованности данных, которые сохраняются в базе данных sharks
. Теперь мы предпримем аналогичный шаг, чтобы обеспечить гарантии для базы данных posts
.
Откройте файл, где определена ваша модель Post
:
Здесь мы хотим убедиться, что посты не пусты и что они не дублируют содержимое, которое могут разместить другие пользователи. Для достижения этого добавьте следующую строку в файл:
class Post < ApplicationRecord
belongs_to :shark
validates :body, presence: true, uniqueness: true
end
Сохраните и закройте файл после завершения редактирования.
После внесенных изменений вы готовы запустить миграции и протестировать приложение.
Во-первых, запустите ваши миграции:
Затем запустите ваш сервер. Если вы работаете локально, вы можете сделать это, запустив:
Если вы работаете на сервере разработки, запустите следующую команду вместо этого:
Перейдите к корню вашего приложения по адресу http://localhost:3000
или http://your_server_ip:3000
.
Учебник по начальному проекту Rails провел вас через добавление и редактирование записи Белая акула. Если вы не добавили других акул, главная страница приложения будет выглядеть так:
Нажмите на Показать рядом с именем Белая акула. Это перенесет вас на представление show
для этой акулы. Вы увидите имя акулы и ее факты, а также заголовок Посты без содержимого. Давайте добавим пост, чтобы заполнить эту часть формы.
Нажмите на Добавить пост под заголовком Посты. Это приведет вас к представлению index
поста, где у вас будет возможность выбрать Новый пост:
Благодаря механизмам аутентификации, которые вы внедрили в Шаге 6 Как создать приложение Ruby on Rails, вас могут попросить аутентифицироваться с использованием имени пользователя и пароля, которые вы создали на этом Шаге, в зависимости от того, создали ли вы новый сеанс или нет.
Нажмите на Новая запись, что приведет вас к вашей шаблону создания записи new
:
В поле Текст введите: “Эти акулы страшные!”
Нажмите на Создать запись. Вы будете перенаправлены на индекс
представление для всех записей, относящихся к этим акулам:
С тем, что наши ресурсы записей работают, мы можем теперь протестировать наши проверки данных, чтобы убедиться, что только желаемые данные сохраняются в базе данных.
Из представления индекс
нажмите на Новая запись. В поле Текст на новой форме попробуйте ввести “Эти акулы страшные!” снова:
Нажмите на Создать запись. Вы увидите следующую ошибку:
Нажмите на Назад, чтобы вернуться на главную страницу записей.
Чтобы протестировать нашу другую валидацию, нажмите на Новая запись снова. Оставьте запись пустой и нажмите Создать запись. Вы увидите следующую ошибку:
С корректно работающими вложенными ресурсами и валидациями, у вас теперь есть работающее приложение Rails, которое вы можете использовать в качестве отправной точки для дальнейшего развития.
Заключение
С вашим приложением Rails вы теперь можете работать над такими вещами, как стилизация и разработка других компонентов фронт-энда. Если вы хотите узнать больше о маршрутизации и вложенных ресурсах, документация Rails – отличное место для начала.
Чтобы узнать больше о интеграции фронт-энд фреймворков с вашим приложением, посмотрите Как настроить проект Ruby on Rails с фронт-эндом React.