Skip to content

Latest commit

 

History

History
449 lines (316 loc) · 26.7 KB

5_lecture.md

File metadata and controls

449 lines (316 loc) · 26.7 KB

5. Мадэлі Rails. Асновы Active Record. Базы дадзеных Rails прыкладанняў. Міграцыі

Мадэлі Rails

У 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 і канфігурацыя Rails-прыкладання

Перад тым, як стварыць базу дадзеных і выканаць міграцыі трэба змяніць канфігурацыю базы дадзеных і усталяваць PostgreSQL на вашу машыну. Ruby інтэрфэйс для працы з PostgreSQL прадастаўляе gem pg. Мы ужо выкарыстоўвалі яго ў production-асяроддзі Heroku. Зараз прыйшоў час ўсталяваць БД лакальна.

Усталёўка гема pg і канфігурацыя прыкладання

Перш за ўсё трэба вынесці 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

Зараз трэба усталяваць 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

Пасля адката не забудзьце зноў выканаць міграцыю.

CRUD

Мы нарэшце стварылі мадэль водгукаў і табліцу у базе дадзеных для захоўвання водгукаў. Мы пакуль што не можам дадаваць водгукі праз вэб-інтэрфэйс нашага прыкладання (трэба яшчэ шмат чаго зрабіць: дадаць 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

Перадача дадзеных мадэлі у View

Давайце створым дзеянні 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

testimonials-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 на якой мы бачым значэнні атрыбутаў нашай зменнай.

Form Builder

Зараз зробім форму старонкі 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

<< папярэдні занятак наступны занятак >>