ActiveRecord поставляется с мощным набором валидаторов и других функций для атрибутов постоянной модели данных. С другой стороны, формы — это один из самых старых и важных строительных блоков современных веб-приложений, необходимый интерфейс для ввода данных пользователем. Из двух помощников форм, которые предоставляет Rails, «form_for» также предполагает, что вы работаете с каким-то постоянным объектом. Таким образом, он может в полной мере использовать все активные функции записи, то есть проверки.
Это все замечательно для постоянных объектов с представлениями на основе базы данных. Но что происходит, когда вам нужна сложная форма, которая не отражает какую-то постоянную запись?
В этом уроке я расскажу о возможных решениях этой проблемы и о том, как ее реализовать в Rails 4 (активные модели).
В этом уроке мы создадим приложение с формой, в которую пользователь может добавить отзыв, который затем будет сохранен в базе данных. Это приложение также будет иметь проверки и представления, точно так же, как вы создаете их для модели на основе базы данных, но затем мы рассмотрим некоторые изменения в модели, чтобы сделать ее без таблиц. И все функции должны работать как есть, без каких-либо дополнительных изменений. Нет действий по обновлению, удалению или поиску обратной связи.
Перво наперво
В этом руководстве я предполагаю, что у вас есть базовое понимание инфраструктуры Rails, и вы можете легко создавать или генерировать базовые контроллеры, модели и представления. Я предполагаю, что вы также знаете немного о том, как работают маршруты и проверки. На момент написания этого руководства я использовал Rails 4.2.5 и SQLite 3.8.10.2 .
Вступление
Может быть много ситуаций, когда у вас есть класс, который вы хотите функционировать как типичная модель ActiveRecord, но вы не хотите сохранять данные в базе данных. Например, у вас может быть контактная форма или что-то более сложное, например, жалоба или форма обратной связи.
В этих ситуациях для решения этой проблемы одним из подходов будет использование вспомогательного метода form_tag , который предоставляет вам настраиваемые поля формы, которые делают то, что вам нужно, без необходимости связывать их с какой-либо моделью вообще.
Это работает хорошо, но form_tag может быстро стать утомительным для написания и поддержки, если обрабатывает более чем несколько полей из-за необходимости обрабатывать имена его многочисленных атрибутов и их проверки самостоятельно. Вскоре ваш контроллер будет иметь дело с большим количеством логики и тоннами параметров формы, что, вероятно, не идеальное решение.
Более чистый и более гибкий подход был бы, если бы мы могли каким-то образом использовать один и тот же form_for с моделью и всеми валидациями и другими привилегиями, с которыми они идут, но без необходимости иметь поддерживаемые базой данных представления ее атрибутов.
Rails предлагает именно такое решение: активную модель, которая похожа на обычную модель, но без таблиц. Он обеспечивает точно такой же простой способ проверки и почти все другие полезности, поставляемые с ActiveRecord. Это помогает поддерживать согласованность структуры приложения, поскольку вы все равно используете модели для представления объектов в приложении, маршруты доступны бесплатно, а построение форм так же просто, как и раньше с form_for .
Давайте сначала создадим новое приложение
На этом этапе мы создадим фиктивное приложение, с которым можно поиграть во время этого урока.
Шаг 1: Сборка
Запустите свой терминал и введите эти команды, чтобы создать новое приложение:
1
2
3
4
5
6
7
8
9
|
# Create a basic Rails App
rails new tableless
cd tableless
# Create a Controller with only new, create & success Actions
rails generate controller feedbacks new create success —skip-routes
# Create a Model
rails generate model feedback name:string email:string address:string message:text suggestion:text
|
Так будет выглядеть ваша структура каталогов .
Шаг 2: Редактирование
Здесь я предоставлю фрагменты кода для всех файлов, которые нужно заполнить. Код довольно понятен. Вы можете загрузить это приложение из репозитория GitHub, связанного с этим сообщением, или выполнить мои действия, чтобы создать его самостоятельно.
→ / config / rout.rb
1
2
|
resources :feedbacks, :only => [:new, :create]
get ‘feedbacks/success’ => ‘feedbacks#success’, as: :success
|
→ /app/views/feedbacks/success.html.erb
1
2
3
|
<h1 id=»notice»><%= notice %></h1>
<br>
<%= link_to ‘Submit New Feedback’, new_feedback_path %>
|
→ / app / views / feedbacks / new.html.erb
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
<h1>New Feedback</h1>
<%= form_for(@feedback) do |f|
<% if @feedback.errors.any?
<div id=»error_explanation»>
<h2><%= pluralize(@feedback.errors.count, «error») %> prohibited this feedback from being saved:</h2>
<ul>
<% @feedback.errors.full_messages.each do |message|
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class=»field»>
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class=»field»>
<%= f.label :email %><br>
<%= f.text_field :email %>
</div>
<div class=»field»>
<%= f.label :address %><br>
<%= f.text_field :address %>
</div>
<div class=»field»>
<%= f.label :message %><br>
<%= f.text_area :message %>
</div>
<div class=»field»>
<%= f.label :suggestion %><br>
<%= f.text_area :suggestion %>
</div>
<div class=»actions»>
<%= f.submit %>
</div>
<% end %>
<%= link_to ‘Back’, feedbacks_path %>
|
→ /app/controllers/feedbacks_controller.rb
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
class FeedbacksController < ApplicationController
def new
@feedback = Feedback.new
end
def create
@feedback = Feedback.new(feedback_params)
respond_to do |format|
if @feedback.save
format.html { redirect_to success_path, notice: ‘Feedback was successfully submitted.’
else
format.html { render :new }
end
end
end
def success
end
private
def feedback_params
params.require(:feedback).permit(:name, :email, :address, :message, :suggestion)
end
end
|
→ / app / models / feedbacks.rb
01
02
03
04
05
06
07
08
09
10
|
class Feedback < ActiveRecord::Base
# fields validation for the database.
validates :name, presence: true
validates :email, presence: true, length: {in:5..255}
validates :address, presence: true
validates :message, presence: true
validates :suggestion, presence: true
end
|
Шаг 3: Развертывание
Чтобы развернуть его на локальном сервере, сначала необходимо выполнить следующие команды для создания базы данных в вашей системе.
1
2
|
cd tableless/
rake db:migrate
|
Если вы следовали этому руководству, команда выше должна создать базу данных sqlite3 по умолчанию. Чтобы изменить его, вы можете перейти к database.yml — ради этого урока я пойду с sqlite3.
Теперь запустите rails s
в вашем терминале, и вы должны увидеть нечто похожее на это.
И с этим вы должны успешно запустить фиктивное приложение.
Шаг 4: Тестирование
Теперь пришло время проверить, что мы только что создали. Нажмите этот маршрут в своем браузере, чтобы проверить, все ли работает хорошо: http://localhost:3000/feedbacks/new
Вы должны увидеть форму, как указано выше. Теперь нажмите кнопку «Отправить», не заполняя ни одного поля, чтобы убедиться, что проверки работают нормально.
Отлично. Вы должны увидеть шесть ошибок валидации, как указано выше. Теперь мы можем попробовать заполнить правильные значения и отправить форму.
Вы должны увидеть нечто похожее на вашем экране. Давайте проверим базу данных на предмет записи, которую мы только что ввели.
Откройте свой Терминал , перейдите в каталог вашего проекта и введите команды ниже.
-
rails db
для запуска клиента базы данных в вашей консоли. -
SQLite> .tables
для отображения всех таблиц в вашей базе данных (по умолчанию выбрана БД). -
SQLite> .headers on
чтобы отображать имена столбцов в ваших результатах. -
SQLite> select * from feedbacks;
чтобы увидеть все отзывы в базе данных.
И здесь мы видим, что отзывы были успешно сохранены в базе данных. Если вы загляните в журналы, вы также можете найти запрос INSERT .
И на этом наше тестирование закончено. Теперь, когда все работает нормально, давайте перейдем к решению.
Решение
Шаг 1: Реализация
Чтобы реализовать Active Model, первое, что вам нужно сделать, это удалить наследование модели обратной связи для < ActiveRecord::Base
как мы больше не хотим, чтобы эта модель имела базу данных.
Как только мы это сделаем, наша форма больше не будет работать, так как валидаторы предоставляются ActiveRecord. Но добавление include ActiveModel::Model
на следующей строке должно восстановить все.
Ваша модель должна выглядеть вот так.
1
2
|
class Feedback
include ActiveModel::Model
|
Второе — добавить attr_accessor
для генерации методов получения и установки для всех атрибутов, например так.
1
|
attr_accessor :name, :email, :address, :message, :suggestion
|
Теперь конечный результат модели должен выглядеть следующим образом.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
class Feedback
include ActiveModel::Model
attr_accessor :name, :email, :address, :message, :suggestion
# fields validation for the database.
validates :name, presence: true
validates :email, presence: true, length: {in:5..255}
validates :address, presence: true
validates :message, presence: true
validates :suggestion, presence: true
end
|
Исправления модели недостаточно, чтобы заставить наше приложение вести себя так, как мы хотим. Контроллер все еще ожидает сохранения полученного объекта данных в базе данных в методе создания . @feedback.save
не будет работать, так как у нас нет базы данных для сохранения новых отзывов.
Мы можем решить эту проблему, изменив @feedback.save
на @feedback.valid?
поскольку в настоящее время мы проводим проверки только в наших моделях, и на основе этого события успеха вы можете выполнить любую предпочитаемую задачу в этом блоке кода, то есть отправлять уведомления, отправлять электронную почту или регистрировать события и т. д.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
class FeedbacksController < ApplicationController
def create
@feedback = Feedback.new(feedback_params)
respond_to do |format|
if @feedback.valid?
# Something interesting can be done here
# — send notifications
# — send email
# — log events
format.html { redirect_to success_path, notice: ‘Feedback was successfully submitted.’
else
format.html { render :new }
end
end
end
|
Шаг 2: Тестирование
Давайте повторим тесты, которые мы выполнили ранее.
Пройдите по маршруту http://localhost:3000/feedbacks/new
и отправьте Форма без заполнения каких-либо полей. Все проверки должны работать как раньше.
Отлично. Теперь мы можем попробовать, отправив форму с допустимыми значениями.
И здесь вы идете — то же самое сообщение об успехе.
Теперь последнее, что нам нужно проверить, — это база данных.
Для этого откройте свой Терминал , перейдите в каталог вашего проекта и введите команды ниже.
-
rails db
для запуска клиента базы данных в вашей консоли. -
SQLite> .tables
для отображения всех таблиц в вашей базе данных (по умолчанию выбрана БД). -
SQLite> .headers on
чтобы отображать имена столбцов в ваших результатах. -
SQLite> select * from feedbacks
чтобы увидеть все отзывы в базе данных.
И на этот раз, поскольку наша модель не поддерживается ни одной таблицей базы данных, вы не найдете вновь представленные значения в таблице.
Если вы проверяете журналы консоли, мы также больше не видим запрос INSERT .
Вывод
Итак, с этим мы покончили с ActiveModel
и увидели, как легко создать модель без таблиц. ActiveModel имеет значительные улучшения, поэтому вы можете ожидать некоторые изменения в следующих версиях Rails.
Мы просто использовали валидации и назначения атрибутов в этом руководстве, чтобы все было просто и понятно. Но взгляните в каталог, который содержит код для ActiveModel на GitHub.
Из списка видно, что ActiveModel также включает в себя, помимо прочего, классы для методов атрибутов, сериализации, обратных вызовов и грязного отслеживания. Таким образом, вы можете следить за будущими функциями, а также знакомиться с другими.