Несмотря на огромное число разнообразных сайтов, практически всю веб-разработку можно свести к CRUD-операциям. В этом уроке мы познакомимся с ними подробнее.
Что такое CRUD
CRUD — это широко распространенный термин, означающий четыре стандартные операции над любой сущностью. Рассмотрим такой пример для статей:
- Создание (Create) — добавление новой статьи.
- Чтение (Read) — просмотр статьи пользователями сайта или в административном интерфейсе.
- Обновление (Update) — редактирование содержимого статьи, изменение заголовка или категории.
- Удаление (Delete) — удаление статьи из базы данных.
Точно так же можно расписать действия над любыми другими ресурсами: комментариями к статьям, тегами, авторами и так далее. Чтобы создать полный CRUD для статей, нужно выполнить следующие действия:
- Создать сущность в коде (как правило, это класс).
- Добавить таблицу в базу данных для хранения статей.
- Написать тесты для проверки обработчиков статей.
- Добавить обработчики для выполнения CRUD операций.
- Добавить шаблоны для отображения статей на сайте.
Ниже мы пройдемся по всему процессу создания CRUD статьи (сущность Article).
Начнем с роутинга. Полный CRUD для статей включает минимум семь маршрутов:
Метод | Маршрут | Шаблон | Описание |
---|---|---|---|
GET | /articles | articles/index.html.erb | Список статей |
GET | /articles/{id} | articles/show.html.erb | Просмотр статьи |
GET | /articles/new | articles/new.html.erb | Форма создания новой статьи |
POST | /articles | Создание новой статьи | |
GET | /articles/{id}/edit | articles/edit.html.erb | Форма редактирования статьи |
PATCH/PUT | /articles/{id} | Обновление статьи | |
DELETE | /articles/{id} | Удаление статьи |
Такое соглашение об именовании маршрутов изначально появилось в Ruby On Rails, а затем его адаптировали во многих других.
Ресурсный роутинг — это механизм в Ruby on Rails, который позволяет легко создавать маршруты для стандартных операций CRUD (Создание, Чтение, Обновление, Удаление) для ресурсов, таких как модели. Он значительно упрощает процесс определения маршрутов, так как автоматически создает все необходимые маршруты для работы с ресурсами.
Командой bin/rails g resource
мы можем создать новый ресурс и полный набор файлов и кода, необходимых для работы с ресурсом. Давайте разберем, что именно делает эта команда и какие файлы она создает.
bin/rails g resource article title:string body:text
invoke active_record
create db/migrate/20250116162806_create_articles.rb
create app/models/article.rb
invoke test_unit
create test/models/article_test.rb
create test/fixtures/articles.yml
invoke controller
create app/controllers/articles_controller.rb
invoke erb
create app/views/articles
invoke test_unit
create test/controllers/articles_controller_test.rb
invoke helper
create app/helpers/articles_helper.rb
invoke test_unit
invoke resource_route
route resources :articles
bin/rails
: Это исполняемый файл Rails, который запускает команды в вашем приложении.g
: Сокращение от "generate", команда для генерации кода.resource
: Указывает, что мы хотитим создать ресурс, который будет включать в себя маршруты, контроллер, представления и миграции.article
: Имя ресурса, в данном случае этоArticle
.title:string body:text
: Это атрибуты, которые будут добавлены к моделиArticle
. Здесьtitle
будет строковым полем, аbody
— текстовым полем.
При выполнении этой команды Rails создаст следующие файлы и изменения:
Миграция: Создается файл миграции в
db/migrate
, который будет содержать код для создания таблицыarticles
с полямиtitle
иbody
.Пример миграции:
class CreateArticles < ActiveRecord::Migration[6.0] def change create_table :articles do |t| t.string :title t.text :body t.timestamps end end end
Модель: Создается файл модели
app/models/article.rb
, который будет представлять сущностьArticle
.Пример модели:
class Article < ApplicationRecord end
Контроллер: Создается контроллер
app/controllers/articles_controller.rb
, который будет обрабатывать запросы, связанные с ресурсомArticle
.class ArticlesController < ApplicationController end
Представления: Создается директория app/views/articles, в которой будут находиться шаблоны представлений для действий контроллера (index, show, new, edit).
Маршруты: В файл config/routes.rb будет добавлена строка для ресурсного роутинга:
resources :articles
Команда генерации упрощает процесс создания нового ресурса в приложении Ruby on Rails, автоматически генерируя все необходимые компоненты для работы с сущностью. После выполнения этой команды останется только выполнить миграцию базы данных с помощью bin/rails db:migrate
и начать использовать созданный ресурс.
Модель была создана пустой, мы можем добавить в нее валидацию присутствия полей:
class Article < ApplicationRecord
validates :title, presence: true
validates :body, presence: true
end
После генерации ресурса Article
с помощью команды bin/rails g resource articles title:string body:text
, у нас есть все необходимые компоненты для работы с CRUD операциями. Давайте подробнее рассмотрим каждую из них.
Создание (Create)
Метод | Маршрут | Метод контроллера |
---|---|---|
POST | /articles | create |
Создание новой статьи начинается с отображения формы для ввода данных. Когда пользователь заполняет форму и отправляет ее, данные передаются на сервер с помощью POST-запроса.
Пример метода create
в контроллере:
class ArticlesController < ApplicationController
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article, notice: 'Статья успешно создана.'
else
render :new
end
end
private
def article_params
params.require(:article).permit(:title, :body)
end
end
В этом методе мы создаем новый объект Article
, используя параметры, полученные из формы с помощью Strong Params. Strong Parameters — это механизм в Ruby on Rails, который помогает защитить приложение от уязвимостей, связанных с массовым присвоением (mass assignment). Он позволяет явно указывать, какие параметры могут быть использованы для создания или обновления объектов, что предотвращает возможность изменения нежелательных атрибутов.
Форма для создания новой статьи отображается на странице, когда пользователь переходит по маршруту GET /articles/new
. В контроллере ArticlesController
метод new
отвечает за инициализацию нового объекта Article
и отображение формы.
class ArticlesController < ApplicationController
# ...
def new
@article = Article.new
end
# ...
end
После выполнения метода new()
, Rails автоматически рендерит шаблон app/views/articles/new.html.erb, который содержит форму для создания новой статьи:
<h1>Создать новую статью</h1>
<%= form_with model: @article, local: true do |form| %>
<% if @article.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@article.errors.count, "ошибка") %>:</h2>
<ul>
<% @article.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div>
<%= form.label :title %>
<%= form.text_field :title %>
</div>
<div>
<%= form.label :body %>
<%= form.text_area :body %>
</div>
<div>
<%= form.submit "Создать" %>
</div>
<% end %><h1>Создать новую статью</h1>
<%= link_to 'Назад к списку', articles_path %>
Здесь Используется хелпер form_with
, который создает форму для объекта @article
. Если при валидации возникли ошибки, они будут отображены в верхней части формы, что позволяет пользователю увидеть, что нужно исправить. Поля формы (title
и body
) автоматически связываются с атрибутами объекта @article
.
Если сохранение прошло успешно, происходит перенаправление на страницу статьи. В противном случае отображается форма с ошибками.
Чтение (Read)
Метод | Маршрут | Метод контроллера |
---|---|---|
GET | /articles | index |
GET | /articles/{id} | show |
Чтение статей включает в себя два действия: отображение списка всех статей и просмотр конкретной статьи.
Пример метода index
:
class ArticlesController < ApplicationController
# ...
def index
@articles = Article.all
end
# ...
end
Переменная @articles
будет доступна в представлении, что позволяет отобразить список статей.
<h1>Список статей</h1>
<table>
<thead>
<tr>
<th>Заголовок</th>
<th>Действия</th>
</tr>
</thead>
<tbody>
<% @articles.each do |article| %>
<tr>
<td><%= link_to article.title, article %></td>
<td>
<%= link_to 'Редактировать', edit_article_path(article) %>
</td>
</tr>
<% end %>
</tbody>
</table>
<%= link_to 'Создать новую статью', new_article_path %>
<%= link_to 'Назад к списку', articles_path %>
Пример метода show
:
class ArticlesController < ApplicationController
# ...
def show
@article = Article.find(params[:id])
end
# ...
end
Метод index
получает все статьи из базы данных и передает их в представление. Метод show
находит конкретную статью по ID и также передает ее в представление.
Обновление (Update)
Метод | Маршрут | Метод контроллера |
---|---|---|
PATCH/PUT | /articles/{id} | update |
Обновление статьи начинается с отображения формы редактирования, которая заполняется текущими данными статьи. После внесения изменений и отправки формы данные передаются на сервер с помощью PATCH или PUT-запроса.
Пример метода update
:
class ArticlesController < ApplicationController
# ...
def update
@article = Article.find(params[:id])
if @article.update(article_params)
redirect_to @article, notice: 'Статья успешно обновлена.'
else
render :edit
end
end
# ...
end
В этом методе мы находим статью по ID и обновляем ее с новыми параметрами. Если обновление прошло успешно, происходит перенаправление на страницу статьи. В противном случае отображается форма редактирования с ошибками.
<h1>Редактировать статью</h1>
<%= form_with model: @article, local: true do |form| %>
<% if @article.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@article.errors.count, "ошибка") %>:</h2>
<ul>
<% @article.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div>
<%= form.label :title %>
<%= form.text_field :title %>
</div>
<div>
<%= form.label :body %>
<%= form.text_area :body %>
</div>
<div>
<%= form.submit "Обновить" %>
</div>
<% end %>
Если при валидации возникли ошибки, они будут отображены в верхней части формы, что позволяет пользователю увидеть, что нужно исправить.
Поля формы (title
и body
) автоматически заполняются текущими значениями статьи.
Удаление (Delete)
Метод | Маршрут | Метод контроллера |
---|---|---|
DELETE | /articles/{id} | destroy |
Удаление статьи происходит по запросу DELETE. Обычно это действие инициируется пользователем через кнопку "Удалить" на странице статьи или в списке статей.
Пример метода destroy
:
class ArticlesController < ApplicationController
# ...
def destroy
@article = Article.find(params[:id])
@article.destroy
redirect_to articles_path, notice: 'Статья успешно удалена.'
end
# ...
end
В этом методе мы находим статью по ID и вызываем метод destroy
, чтобы удалить ее из базы данных. После успешного удаления происходит перенаправление на страницу со списком статей.
Чтобы выполнить запрос на удаление ресурса из шаблона, мы должны использовать хелпер link_to
с указанием метода :delete
. Это позволяет отправить DELETE-запрос на сервер, когда пользователь нажимает на ссылку.
<%= link_to 'Удалить', article, data: { turbo_method: :delete, turbo_confirm: 'Вы уверены, что хотите удалить эту статью?' } %>
Flash
Сообщения flash используются для отображения временных уведомлений пользователям, таких как сообщения об успехе или ошибках после отправки форм или выполнения других действий. Флеш-сообщения передаются в шаблон и там выводятся. После их извлечения хранилище обнуляется.
Флеш-сообщения показываются только на один запрос. После обновления страницы или перехода в другое место они пропадают. Это удобно, так как не нужно следить за их жизненным циклом.
Во многих веб-фреймворках, включая Ruby on Rails, типы флеш-сообщений часто стандартизированы, например:
notice или success: для информирования пользователя о том, что действие было выполнено успешно.
error для отображения ошибок, связанных с валидацией или другими проблемами. Например, сообщение о том, что введённые данные некорректны.
info: Используется для предоставления дополнительной информации пользователю, которая не является ни успешной, ни ошибочной. Например, сообщение о том, что действие будет выполнено позже.
warning: для предупреждения пользователя о потенциальных проблемах или рисках, связанных с его действиями.
Пример использования Flash
В контроллере Ruby on Rails мы можем использовать эти типы сообщений следующим образом:
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article, notice: 'Статья успешно создана.'
else
flash.now[:alert] = 'Ошибка при создании статьи. Пожалуйста, проверьте данные.'
render :new
end
end
Чтобы вывести flash-сообщения в нашем приложении на Ruby on Rails, добавим соответствующий код в представление. Обычно это делается в основном макете приложения, чтобы сообщения отображались на всех страницах:
app/views/layouts/application.html.erb:
<% if flash.any? %>
<% flash.each do |key, value| %>
<div class="<%= key %>"><%= value %></div>
<% end %>
<% end %>
Собираем все вместе
Итоговый контроллер: Полный разбор элементов CRUD
Собираем все элементы CRUD в итоговом контроллере ArticlesController
, который управляет статьями в нашем приложении. Контроллер включает следующие действия:
- create: Создает новую статью, проверяет наличие ошибок и перенаправляет пользователя на страницу статьи или возвращает форму с сообщением об ошибках.
- new: Инициализирует новый объект статьи для отображения формы создания.
- index: Получает все статьи и отображает их в списке.
- show: Находит и отображает конкретную статью по ее идентификатору.
- edit: Находит статью для редактирования и отображает форму.
- update: Обновляет существующую статью, проверяет наличие ошибок и перенаправляет пользователя или возвращает форму с сообщениями об ошибках.
- destroy: Удаляет статью и перенаправляет пользователя на список статей или возвращает сообщение об ошибке, если удаление не удалось.
class ArticlesController < ApplicationController
def create
@article = Article.new(article_params)
if @article.save
flash[:notice] = 'Статья успешно создана'
redirect_to @article
else
flash[:notice] = 'Упс, поправьте ошибки в форме'
render :new, status: :unprocessable_entity
end
end
def new
@article = Article.new
end
def index
@articles = Article.all
end
def show
@article = Article.find(params[:id])
end
def edit
@article = Article.find(params[:id])
end
def update
@article = Article.find(params[:id])
if @article.update(article_params)
flash[:notice] = 'Статья успешно обновлена'
redirect_to @article
else
flash[:notice] = 'Упс, поправьте ошибки в форме'
render :edit, status: :unprocessable_entity
end
end
def destroy
@article = Article.find(params[:id])
if @article.destroy
flash[:notice] = 'Статья успешно удалена'
redirect_to articles_path
else
flash[:notice] = 'Упс, не получилось удалить статью'
redirect_back fallback_location: :articles_path
end
end
private
def article_params
params.require(:article).permit(:title, :body)
end
end
Контроллер также включает метод article_params
, который использует Strong Parameters для обеспечения безопасности, разрешая только определенные параметры для массового присвоения.
Обработка HTTP-ошибок
Обработка HTTP-ошибок позволяет обеспечить пользователям понятные и информативные сообщения об ошибках. В Ruby on Rails существует несколько способов обработки ошибок, которые позволяют разработчикам управлять различными ситуациями, такими как отсутствие запрашиваемых ресурсов, ошибки валидации и внутренние серверные ошибки.
Рассмотрим, как настроить маршруты для обработки ошибок, создать контроллер для управления этими ошибками и использовать механизмы Rails для перенаправления пользователей на соответствующие страницы ошибок.
Настройка обработки ошибок
В Ruby on Rails настройка
config.exceptions_app = routes
в config/application.rb позволяет нам обрабатывать ошибки через маршруты приложения, что дает возможность создавать свои страницы ошибок.В файле
config/application.rb
добавим следующую строку:config.exceptions_app = routes
Это указывает фреймворку использовать маршруты приложения для обработки исключений.
В файле
config/routes.rb
добавим маршруты для обработки ошибок:Rails.application.routes.draw do get '/404', to: 'errors#not_found', as: :not_found_errors get '/500', to: 'errors#server_error', as: :server_error_errors # Обработка всех остальных маршрутов match '*unmatched', to: 'errors#not_found', via: :all end
Создадим контроллер
ErrorsController
в соответствующем файле app/controllers/errors_controller.rb:class ErrorsController < ApplicationController def not_found render status: :not_found end def server_error render status: :internal_server_error end end
Создадим директорию
app/views/errors
и добавим в нее шаблоны для ошибок:app/views/errors/not_found.html.erb:
<!-- app/views/errors/not_found.html.erb --> <h1>404 - Страница не найдена</h1> <p>Извините, но запрашиваемая страница не существует.</p> <%= link_to 'Вернуться на главную', root_path %>
app/views/errors/server_error.html.erb:
<h1>500 - Внутренняя ошибка сервера</h1> <p>Извините, произошла ошибка на сервере. Пожалуйста, попробуйте позже.</p> <%= link_to 'Вернуться на главную', root_path %>
Поведение вывода ошибок зависит от конфигурации окружения, в котором работает приложение. Если config.consider_all_requests_local
установлено в true
(что является значением по умолчанию в development-среде), Rails будет показывать полные трассировки стека и подробные сообщения об ошибках в браузере. Это позволяет видеть, что пошло не так, и быстро исправлять ошибки.
В production-среде consider_all_requests_local
обычно установлено в false
. Rails не будет показывать полные трассировки стека. Вместо этого фреймворк будет отображать стандартные страницы ошибок (например, 404 или 500) или наши страницы ошибок.
Изменить поведение вывода ошибок в разных окружениях, можно изменив config/environments/.rb* файлы. Например, в config/environments/development.rb:
require 'active_support/core_ext/integer/time'
Rails.application.configure do
# ...
# Теперь будут выводиться страницы из представлений views/errors/*
config.consider_all_requests_local = false
# ...
end
Теперь наше приложение настроено для обработки ошибок через маршруты, что позволяет предоставлять пользователям более информативные и стилизованные страницы ошибок. Это улучшает взаимодействие с приложением и делает его более удобным для пользователей. Мы можем дополнительно настроить стили и содержание этих страниц в зависимости от требований и дизайна приложения.
Заключение
Мы изучили создания CRUD-сущности в Ruby on Rails, мы рассмотрели ключевые этапы, включая настройку ресурсного роутинга, создание контроллеров и представлений, а также обработку HTTP-ошибок. Эти аспекты обеспечивают функциональность, удобство использования и надежность веб-приложения.
Самостоятельная работа
Создайте новый Rails-проект для экспериментов, если его еще нет.
Повторите примеры из теории. С помощью генераторов создайте CRUD для сущности Article. У сущности должны быть следующие поля
- title - текстовая строка, минимум 10 символов
- body - текст. Минимум 200 символов
- author - текстовая строка, имя автора
- published? - поле-флаг с признаком публикации
На главную страницу добавьте ссылку на список статей.
Дополнительные материалы
- Getting Started with Rails - CRUD
- Rails Guides - Rails Routing from the Outside In
- Как использовать faker для заполнения таблиц в Rails?
- Faker
- CRUD глаголы и экшены
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.