У RESTful web-прыкладаннях звычайна кантроллер атрымлівае запыт, дастае альбо захоўвае дадзеныя у мадэлі і выкарыстоўвае View для стварэння адказа у выглядзе HTML. Такім чынам, кантроллер з'яўляецца пасярэднікам паміж мадэлямі і прадстаўленнямі. Ён робіць дадзеныя мадэлі даступнымі у View і, адпаведна, даступнымі для карыстальніка. Таксама кантроллер можа захоўваць і абнаўляць дадзеныя ад карыстальніка у мадэль.
Прыйшоў час стварыць мадэлі ў нашым прыкладанні. Зараз ўжо відавочна, што нам спатрэбіцца мадэль дадзеных для водгукаў (Testimonial). Таксама я вырашыў стварыць старонку са спісам студэнтаў. Таму з'яўляецца яшчэ адна існасць - Student. Водгук будзе складацца з нікнэйма карыстальніка (user_name: string) і ўласна тэкста водгука (feedback:text). Для мадэлі студэнта пакуль створым адно поле з нікам на Github (github_user: string). Пакуль спынімся на гэтых дзвюх мадэлях. Скажу наперад, што Rails дазваляе даволі гнутка маніпуляваць мадэлямі і змяняць структуру дадзеных мадэлі.
Але для таго, каб працаваць з дадзенымі, недастаткова мадэлі. Трэба яшчэ знайсці спосаб захаваць гэтыя дадзеныя. Значыць прыйшоў час працы з базамі дадзеных. Rails прадастаўляе зручную бібліятэку з прыемным сінтаксісам Active Record для ўзаемадзеяння з базамі дадзеных. Дзякуючы Active Record вам не трэба пісаць спецыфічныя запыты, таму значна прасцей будзе змяніць БД пад час распрацоўкі, калі ў гэтым будзе патрэба. Таксама, Rails дазваляе пісаць міграцыі для азначэння данных проста на Ruby. Такім чынам, прасунутыя веды SQL (калі вы выкарыстоўваеце рэляцыйную БД) для узаемадзеяння з дадзенымі вам не патрэбны. Але, безумоўна, базавыя веды SQL і уладкавання БД павінен мець кожны распрацоўшчык, бо пісменная арганізацыя ўзаемадзеяння прыкладання з БД з'яўляецца асновай высокапрадукцыйнага прыкладання.
Ствараем мадэль для водгукаў (мы ужо вызначыліся, што гэта мадэль будзе мець радковы атрыбут user_name і тэкставы атрыбут feedback):
$ rails generate model Testimonial user_name:string feedback:text --no-test-framework
Апроч файла мадэлі app/models/testimonial.rb згенераваўся файл міграцыі, прычым у назву гэтага файла уключаны прэфікс з часовым цэтлікам, які адлюстроўвае час стварэння міграцы (гэта дазваляе захоўваць храналогію змянення схемы БД і палягчае працу у камандзе). Міграцыя уяўляе сабой змяненне, якое трэба зрабіць з дадзенымі, незалежна ад базы дадзеных, якая ужываецца. Міграцыі даюць магчымасць паступовага змянення структуры БД. Вам не трэба загадзя прадумваць усю структуру захоўвання дадзеных. Такім чынам, распрацоўка робіцца вельмі гнуткай.
Звярніце увагу, што клас мадэлі і назва файла у адзіночным ліку, у сваю чаргу, назва міграцыі і табліцы БД - у множным. Усё лагічна: мадэль прадстаўляе адзін аб'ект (у дадзеным выпадку, водгук), а табліца захоўвае ўсе водгукі.
Перад тым, як стварыць базу дадзеных і выканаць міграцыі трэба змяніць канфігурацыю базы дадзеных і усталяваць PostgreSQL на вашу машыну. Ruby інтэрфэйс для працы з PostgreSQL прадастаўляе gem pg. Мы ужо выкарыстоўвалі яго ў production-асяроддзі Heroku. Зараз прыйшоў час ўсталяваць БД лакальна.
Перш за ўсё трэба вынесці gem pg з групы production.
# Gemfile
gem 'pg'
Магчыма, спатрэбіцца усталяваць дадатковую бібліятэку для канфігурацыі postgresql:
$ sudo apt-get install libpq-dev
Не забудзьце выдаліць з гемфайла sqlite3 - ён больш не патрэбны. Усталёўваем:
$ bundle
Далей трэба абнавіць канфігурацыйны файл базы дадзеных нашага прыкладання. Будзьце увыжлівы. Фармат YAML вельмі адчувальны да прабелаў. Кожны узровень у іерархіі аддзяляецца двума прабеламі:
# config/database.yml
default: &default
adapter: postgresql
encoding: unicode
pool: 5
development:
<<: *default
database: course-app_development
test:
<<: *default
database: course-app_test
production:
<<: *default
database: course-app_production
host: localhost
Зараз трэба усталяваць PostgreSQL на вашу сістэму:
$ sudo apt-get install postgresql
Пасля усталёўкі заходзім у кансоль пад юзерам postgres:
$ sudo su postgres
Зараз мы наладзім postgresql такім чынам, каб не давялося кожны раз дадаваць прывілеі нашаму чарговаму прыкладанню і не трэба было яўна прапісваць у наладках БД прыкладання імя карыстальніка і пароль. Мы проста дадзім дазвол нашаму асноўнаму карыстальніку Linux (а менавіта пад ім і працуе Rails-прыкладанне ў асяроддзі development) маніпуяваць базамі дадзеных Postgresql:
$ createuser -s micrum
Тут micrum - гэта імя майго асноўнага карыстальніка Linux. Замяніце яго на свайго карыстальніка. Флаг -s значыць, што карыстальнік мае прывілеі суперюзера.
Зараз перайдзіце у каталог прыкладання пад звычайным карыстальнікам, у маім выпадку micrum (каб выйсці з-пад postgres трэба націснуць Ctrl+D). Нарэшце можна стварыць базу:
$ rake db:create
Схема базы дадзеных пакуль што пустая. Зараз мы выканаем міграцыю і створым табліцу у базе:
$ rake db:migrate
Выкананне міграцыі прыводзіць да стварэння табліцы testimonials з двума слупкамі:
user_name і feedback. Міграцыя уяўляе з сябе метад change, які апісвае
змяненні, якія ўносяцца ў БД. У сваю чаргу, метад change выкарыстоўвае
Rails-метад create_table для стварэння табліцы testimonials у БД. У блоке
гэтага метада прапісаны слупкі з тыпамі дадзеных, а таксама ёсць
радок t.timestamps null: false
. Дзякуючы гэтаму радку у нашай табліцы ствраюцца
дадатковыя слупкі created_at і updated_at. У гэтыя слупкі аўтаматычна
запісваецца час стварэння і абнаўлення кожнага аб'екта.
Паглядзіце файл схемы базы дадзеных db/schema.rb. Кожная міграцыя абнаўляе схему: дадаюцца/выдаляюцца слупкі табліцы, ствараюцца новыя табліцы і гэтак далей. За абнаўленнем схемы сочыць Active Record.
Вы таксама можаце адкаціць міграцыю. У дадзеным выпадку толькі што створаная табліца выдаліцца. Заўсёды правярайце вашы міграцыі на магчымасць адката.
$ rake db:rollback
Пасля адката не забудзьце зноў выканаць міграцыю.
Мы нарэшце стварылі мадэль водгукаў і табліцу у базе дадзеных для захоўвання водгукаў. Мы пакуль што не можам дадаваць водгукі праз вэб-інтэрфэйс нашага прыкладання (трэба яшчэ шмат чаго зрабіць: дадаць views, actions, абнавіць маршруты), але папрацаваць з дадзенымі ужо дужа хочацца. Таму скарыстаемся кансолью Rails.
Rails кансоль - гэта утыліта каманднага радка для працы з Rails прыкладаннямі. Запускаем кансоль:
$ rails console
Зараз мы можам узаемадзейнічаць з нашым прыкладаннем і маніпуляваць дадзенымі. Давайце створым першы водгук:
> testimonial = Testimonial.new(user_name: 'Max Planck', feedback: 'The energy of oscillators in a black body is quantized')
Мы стварылі зменную testimonial і выклікалі метад new на класе Testimonial,
а таксама перадалі ў дужках атрыбуты user_name і feedback). Вы, магчыма,
звярнулі увагу, што у нашага створанага аб'екта атрыбуты id, created_at і
updated_at роўныя nil. Справа ў тым, што выклік Testimonial.new
не
захоўвае у базе дадзеных ваш запіс, а толькі стварае Ruby-аб'ект у памяці.
Каб захаваць ў базу водгук, трэба выканаць метад save на нашай зменнай, якая з'яўляецца Ruby-абъектам:
> testimonial.save
Звярніце ўвагу на вынік гэтай каманды: вы бачыце вялікі SQL-запыт. Дзякуючы Active Record, вам не трэба пісаць гэты вялізны кусок кода, а дастаткова проста выканаць лаканічны метад save. Больш за тое, Active Record дазваляе працаваць не толькі з SQL-базамі і выконвае за вас ўсю працу па ўзаемадзеянню непасрэдна з БД.
Давайце створым яшчэ адзін запіс:
> Testimonial.new(user_name: 'Albert Einstein', feedback: 'The speed of light in a vacuum is the same for all observers, regardless of the motion of the light source').save
Звярніце увагу, што зараз мы не стваралі зменную, а паслядоўна выканалі метады new і save
Можна зрабіць яшчэ прасцей: выклікаць метад create, які не толькі створыць, але і захавае наш аб'ект:
> Testimonial.create(user_name: 'Isaac Newton', feedback: 'Any two bodies in the universe attract each other with a force that is directly proportional to the product of their masses and inversely proportional to the square of the distance between them')
Давайце паглядзім, як можна атрымаць доступ да аб'ектаў з базы дадзеных. Для таго, каб атрымаць калекцыю усіх водгукаў, можна скарыстацца метадам Active Record all:
> Testimonial.all
Звярніце увагу, што кожны запіс мае свой унікальны id. Кожны новы запіс мае інкрэментальна павялічанае значэнне id. Таксама, мы бачым, што аб'екты ужо маюць значэнні атрыбутаў created_at і updated_at.
Мы можам выцягнуць з базы дадзеных асобны запіс, напрыклад, зрабіць гэта можна праз пошук па id:
> Testimonial.find(2)
Таксама, можна прачытаць значэнне канкрэтнага атрыбута аб'екта, напрыклад user_name для водгука з id роўным 1:
> Testimonial.find(1).user_name
Вы можаце выцягнуць з базы апошні запіс:
> Testimonial.last
Падрабязней пра запыты Active Record чытайце у Rails Guides.
Мы можам таксама абнащляць абъекты, напрыклад, змяніць імя для першага водгука:
> testimonial = Testimonial.first
> testimonial.update_attribute(:user_name, 'Crazy Sciencist')
Таксама можна выдаліць запіс з базы сродкамі Active Record. Давайце знойдзем водгук з імем 'Crazy Sciencist' і выдалім яго:
> Testimonial.find_by(user_name: 'Crazy Sciencist').destroy
Такім, чынам, мы можам выканаць усе чатыры рэсурсныя аперацыі над аб'ектам: CREATE, READ, UPDATE, DESTROY з дапамогай сродкаў Active Record.
Мы выкарыстоўваем PostgreSQL - гэта рэляцыйная база дадзеных. Наша база пакуль што уключае ў сябе толькі адну табліцу testimonials, якая адлюстроўваецца на клас мадэлі Testimonial. У сваю чаргу, табліца складаецца з радкоў дадзеных. Кожны радок адлюстроўваецца на канкрэтны аб'ект мадэлі (у нашым выпадку кожны радок - гэта водгук). І, нарэшце, кожны радок мае слупкі з атрыбутамі аб'екта (у нашым выпадку feedback i user_name).
Такое адлюстроўванне Ruby класаў на табліцы БД адпавядае паттэрну ORM і менавіта дзякуючы гэтаму адлюстраванню, нам не трэба пісаць SQL-запыты. Узаемадзеянне з базай дадзеных адбываецца праз Ruby DSL, якім з'яўляецца Active Record
Перад тым, як зрабіць форму для водгука на нашым сайце, давайце створым мадэль Student, як дамаўляліся. Потым вернемся да яе.
$ rails generate model Student github_user:string --no-test-framework
У нас згенераваўся новы файл міграцыі. Кожны раз, пры стварэнні міграцыі, яе трэба выконваць, каб абнавіць схему:
$ rake db:migrate
Давайце створым дзеянні show і new Testimonials кантроллера:
# app/controllers/testimonials_controller.rb
class TestimonialsController < ApplicationController
def index
end
def new
@testimonial = Testimonial.new
end
def show
@testimonial = Testimonial.find(params[:id])
end
end
У метадзе show мы ствараем зменную экземпляра кантроллера, якая роўная ўжо
вядомаму вам метаду find. Але мы не указваем яўна id аб'екта, замест
гэтага бачым запіс params[:id]
. Усё правільна. Мы хочам, каб show дзеянне
паказвала нам старонку з канкрэтным водгукам. Але як кантроллер даведаецца, які
водгук мы хочам убачыць? Мы можам наўпрост перадаць параметр id ў запыце да
прыкладання праз URI старонкі.
Але спачатку трэба абнавіць нашы рэсурсныя маршруты:
#config/routes.rb
resources :testimonials, only: [:index, :new, :show]
Паглядзіце, як выглядаюць зараз маршруты:
$ rake routes
Мы бачым, што show дзеянню адпавядае URI тыпа testimonials/:id. То бок, калі вы зробіце запыт http://0.0.0.0:3000/testimonials/2 , то зможаце пабачыць водгук з id=2. Але спачатку трэба дадаць адпаведны шаблон View:
#app/views/testimonials/show.html.erb
<div class="page-header">
<h2><%= @testimonial.user_name %></h2>
<q><%= @testimonial.feedback %></q>
</div>
У шаблон мы перадаем значэнні user_name і feedback нашай зменнай экземпляра кантроллера. Зараз можна ў браўзеры атрымаць дадзеныя мадэлі, напрыклад http://0.0.0.0:3000/testimonials/2
Гэта і ёсць MVC у дзеянні: мы робім GET запыт, роўтэр глядзіць URI і параўноўвае яго з патэрнамі даступных маршрутаў, затым накіроўвае запыт на дзеянне show кантроллера Testimonials. У гэтым дзеянні у зменную @testimonial кладзецца аб'ект мадэлі Testimonial з id=2, пасля гэтага кантроллер вяртае нам старонку show.html.erb на якой мы бачым значэнні атрыбутаў нашай зменнай.
Зараз зробім форму старонкі new праз якую зможам ствараць новыя запісы ў базе дадзеных непасрэдна праз браўзер:
#app/views/testimonials/new.html.erb
<div class="page-header">
<h1>New testimonial</h1>
</div>
<%= form_for(@testimonial) do |f| %>
<%= f.label :user_name %>
<%= f.text_field :user_name %>
<%= f.label :feedback %>
<%= f.text_field :feedback %>
<%= f.submit 'Create testimonial', class: 'btn btn-large btn-primary' %>
<% end %>
Для стварэння формы мы выкарысталі Form Builder
Мы выклікалі form_for і перадалі у гэты метад аб'ект @testimonial. Унутры блока для гэтага метада мы ствараем тэкставыя палі і лэйблы для іх. Зараз можна паглядзець, што атрымалася: http://0.0.0.0:3000/testimonials/new
Мы не напісалі ані радка html, але маем гатовую форму дзякуючы хэлперам форм Rails. Падрабязней - глядзім гайды.
Але пакуль што форма не працуе. Каб запосціць форму, трэба, па-першае, дадаць маршрут create:
# config/routes.rb
resources :testimonials, only: [:index, :new, :show, :create]
Па-другое, трэба дадаць create дзеянне, каб захоўваць у базе водгукі:
# app/controllers/testimonials_controller.rb
def create
@testimonial = Testimonial.new(testimonial_params)
if @testimonial.save
redirect_to @testimonial
else
render 'new'
end
end
private
def testimonial_params
params.require(:testimonial).permit(:user_name, :feedback)
end
У метадзе create мы ствараем аб'ект мадэлі Testimonial з пэўнымі параметрамі testimonial_params. Гэтыя параметры мы вынеслі у асобны прыватны метад testimonial_params, у якім пазначылі белы спіс параметраў кантроллера. У гэтым спісе пералічаны два атрыбута, якія карыстальнік можа перадаваць з POST-запытам адпраўкі формы: user_name і feedback. Такім чынам мы абараняем прыкладанне: запыт ад карыстальніка уключае толькі два гэтых атрыбута і нічога больш.
У метадзе create мы бачым канструкцыю if ... else ... end
якая правярае,
цi захаваўся водкук у базе. Калі так - то адбываецца перанакіраванне на show
дзеянне, калі не - зноў вяртаецца форма дзеяння new.
Зараз мы можам ствараць водгукі праз браўзер: http://0.0.0.0:3000/testimonials/new.
Але мы можам захаваці і пусты аб'ект, але гэта непрыймальна. Для таго, каб праверыць стан аб'екта да адпраўкі у базу дадзеных існуюць валідацыі Active Record.
Зробім праверку водгука на наяўнасць user_name:
# app/models/testimonial.rb
class Testimonial < ActiveRecord::Base
validates :user_name, :feedback, presence: true
end
Зараз у вас ужо не атрымаецца адправіць пустую форму. Правярайце. Таксама, на будучыню, зробім праверку аб'ектаў мадэлі Student на унікальнасць атрыбута github_user:
# app/models/student.rb
class Student < ActiveRecord::Base
validates :github_user, presence: true, uniqueness: true
end
Больш пра валідацыі - Rails Guides.
Міграцыі дазваляюць не толькі ствараць табліцы, але і змяняць іх структуру з цягам часу. Гэта вельмі добра, бо падчас распрацоўкі патрабаванні да захоўвання дадзеных могуць змяніцца. І вам не трэба жорстка вызначацца са структурай базы з самага пачатку. Можна проста паступова нарошчваць схему базы і выдаляць лішняе.
Я вырашыў дадаць яшчэ адну калонку у табліцу students. Гэта не праблема - дастаткова згенераваць міграцыі. Ніколі не стварайце міграцыі уручную - карыстайцеся генератарам, які аўтаматычна дадасць цэтлік часу у назву файла:
$ rails g migration AddNameToStudents
Зараз трэба знайсці файл міграцыі (у вас назва будзе адрознівацца акурат на згенераваны часовы цэтлік). Дададзім метад add_column, які стварае калонку у табліцы.
# db/migrate/20150402182133_add_name_to_students.rb
class AddNameToStudents < ActiveRecord::Migration
def change
add_column :students, :name, :string
end
end
Не забываем выканаць міграцыю! Падрабязней пра міграцыі чытайце самі ведаеце дзе
Дарэчы, для таго, каб прагнаць міграцыі на Heroku, трэба выканаць:
$ heroku run rake db:migrate