Статьи

Модели не-ActiveRecord в Rails 4

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
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

Так будет выглядеть ваша структура каталогов .

Структура каталогов

Здесь я предоставлю фрагменты кода для всех файлов, которые нужно заполнить. Код довольно понятен. Вы можете загрузить это приложение из репозитория 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

Чтобы развернуть его на локальном сервере, сначала необходимо выполнить следующие команды для создания базы данных в вашей системе.

1
2
cd tableless/
rake db:migrate

Если вы следовали этому руководству, команда выше должна создать базу данных sqlite3 по умолчанию. Чтобы изменить его, вы можете перейти к database.yml ради этого урока я пойду с sqlite3.

Теперь запустите rails s в вашем терминале, и вы должны увидеть нечто похожее на это.

консольный журнал

И с этим вы должны успешно запустить фиктивное приложение.

Теперь пришло время проверить, что мы только что создали. Нажмите этот маршрут в своем браузере, чтобы проверить, все ли работает хорошо: http://localhost:3000/feedbacks/new

Страница обратной связи

Вы должны увидеть форму, как указано выше. Теперь нажмите кнопку «Отправить», не заполняя ни одного поля, чтобы убедиться, что проверки работают нормально.

Ошибки обратной связи

Отлично. Вы должны увидеть шесть ошибок валидации, как указано выше. Теперь мы можем попробовать заполнить правильные значения и отправить форму.

Успешная обратная связь

Вы должны увидеть нечто похожее на вашем экране. Давайте проверим базу данных на предмет записи, которую мы только что ввели.

Откройте свой Терминал , перейдите в каталог вашего проекта и введите команды ниже.

  • rails db для запуска клиента базы данных в вашей консоли.
  • SQLite> .tables для отображения всех таблиц в вашей базе данных (по умолчанию выбрана БД).
  • SQLite> .headers on чтобы отображать имена столбцов в ваших результатах.
  • SQLite> select * from feedbacks; чтобы увидеть все отзывы в базе данных.
Просмотр всех отзывов в базе данных

И здесь мы видим, что отзывы были успешно сохранены в базе данных. Если вы загляните в журналы, вы также можете найти запрос INSERT .

Просмотр журнала базы данных

И на этом наше тестирование закончено. Теперь, когда все работает нормально, давайте перейдем к решению.

Чтобы реализовать 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

Давайте повторим тесты, которые мы выполнили ранее.

Пройдите по маршруту http://localhost:3000/feedbacks/new и отправьте Форма без заполнения каких-либо полей. Все проверки должны работать как раньше.

Тестирование отправки отзывов

Отлично. Теперь мы можем попробовать, отправив форму с допустимыми значениями.

Отправка формы с действительными значениями

И здесь вы идете — то же самое сообщение об успехе.

Успешная отправка обратной связи

Теперь последнее, что нам нужно проверить, — это база данных.

Для этого откройте свой Терминал , перейдите в каталог вашего проекта и введите команды ниже.

  • rails db для запуска клиента базы данных в вашей консоли.
  • SQLite> .tables для отображения всех таблиц в вашей базе данных (по умолчанию выбрана БД).
  • SQLite> .headers on чтобы отображать имена столбцов в ваших результатах.
  • SQLite> select * from feedbacks чтобы увидеть все отзывы в базе данных.

И на этот раз, поскольку наша модель не поддерживается ни одной таблицей базы данных, вы не найдете вновь представленные значения в таблице.

Нет значений в базе данных

Если вы проверяете журналы консоли, мы также больше не видим запрос INSERT .

Консольные логи

Итак, с этим мы покончили с ActiveModel и увидели, как легко создать модель без таблиц. ActiveModel имеет значительные улучшения, поэтому вы можете ожидать некоторые изменения в следующих версиях Rails.

Мы просто использовали валидации и назначения атрибутов в этом руководстве, чтобы все было просто и понятно. Но взгляните в каталог, который содержит код для ActiveModel на GitHub.

Код ActiveModel на GitHub

Из списка видно, что ActiveModel также включает в себя, помимо прочего, классы для методов атрибутов, сериализации, обратных вызовов и грязного отслеживания. Таким образом, вы можете следить за будущими функциями, а также знакомиться с другими.