active record basics
TRANSCRIPT
Active Record BasicsRoR Lab BiWeekly Lecture
남승균 - RoR Lab
모든 초고는 쓰레기다- 어네스트 헤밍웨이
2/72
먼저 소설가가 되어야만 소설을 쓸 수 있는 게 아니라 먼저뭔가를 써야만 소설가가 될 수 있다.- 김연수, <소설가의 일> 중
3/72
'초고' -> '처음 작성한 코드''소설' -> '프로그램(코드)''소설가' -> '프로그래머'로 치환하면?
작동하는 코드를 만들고, 그 코드를 점진적으로 개선하는 일- <프로그래머(개발자)의 일>
4/72
AgendaActive Record: IntroductionCreating Active Record ModelCRUD: Reading and Writing DataConvention in Active RecordOverriding the Naming ConventionsMigrations : 살짝 맛보기Validations : 살짝 맛보기Callbacks : 살짝 맛보기
········
5/72
Active Record: IntroductionWhat is Active Record
Rails MVC 중 'M(Model)'의 역할을 담당비즈니스 데이터와 로직을 다루는 역할을 하는 시스템 레이어ORM(Object Relational Mapping)의 Active Record 패턴을 구현한 구현체
···
6/72
Active Record: IntroductionThe Active Record Pattern
Patterns of Enterprise Application Architecture, by Martin Fowler
Data와 Behavior를 Object가 보유, 관리+ Data access logic을 도메인 오브젝트가 수행
··
7/72
Active Record: Introductionas an ORM Framework
표현(Represents):
검증(Validates):
수행(Performs):
·모델(Model)과 데이터를 표현모델간의 연관관계(associations)를 표현관련 모델을 통한 상속 계층(inheritance hierachies)를 표현
···
·데이터베이스에 저장하기 전 모델을 검증·
·객체지향(Object-Oriented) 방식으로 데이터베이스 명령을 수행·
8/72
ORM Huddle
ORM frameworks need
A lot of "CONFIGURATION CODE"in general
9/72
SolutionRails Way
Convention over ConfigurationFollow the conventions adopted by Rails
very little configurationno configuration in some case
··
10/72
Creating Active Record ModelPreparing MySQL Database
CREATE DATABASE ar_basics_development;CREATE TABLE products ( id int(11) NOT NULL auto_increment, name varchar(255), vender varchar(255), PRIMARY KEY (id))
SQL
Table 명이 products (복수형: Plural Form)인 것에 유의Naming Convention과 Customizing은 뒤에서 다룰 예정
··
11/72
Creating Active Record ModelPreparing Rails Application
rails new ar_basics SHELL
# in config/database.ymldevelopment: adapter: mysql2 encoding: utf8 database: ar_basics_development username: root password:
YML
12/72
Creating Active Record ModelComnecting to MySQL
# in Gemfilegem "mysql2"
bundle install SHELL
13/72
Creating Active Record ModelCreating Model
# in app/models/product.rbclass Product < ActiveRecord::Baseend
RUBY
Model 명이 product (단수형: Singular Form)인 것에 유의·
14/72
CRUD: Reading and Writing DataRunning Rails Console
# in {RAILS_ROOT}rails console# orrails c
SHELL
레일스 애플리케이션과 상호작용웹사이트를 이용하지 않고 서버의 데이터를 빠르게 변경
··
15/72
CRUD: Reading and Writing DataCreate
# Using create methodproduct = Product.create(name: "iPhone 6", vender: "Apple")
RUBY
새 모델을 만들어 반환데이터베이스에 저장
··
16/72
CRUD: Reading and Writing DataCreate
# Using new methodproduct = Product.newproduct.name = "iPhone 6"product.vender = "Apple"
product.save
RUBY
새 모델을 만들어 반환save 메서드가 호출되기 전까지는 데이터베이스에 저장되지 않음
··
17/72
CRUD: Reading and Writing DataCreate
# Using new method with blockproduct = Product.new do |p| p.name = "iPhone 6" p.vender = "Apple"end
product.save
RUBY
새 모델을 만들어 반환save 메서드가 호출되기 전까지는 데이터베이스에 저장되지 않음
··
18/72
CRUD: Reading and Writing DataRead
# return a collection with all productsproducts = Product.all
RUBY
# => equivalent to:SELECT `products`.* FROM `products`
SQL
products 테이블 내 모든 데이터 컬렉션을 반환·
19/72
CRUD: Reading and Writing DataRead
count = Product.count RUBY
# => equivalent to:SELECT COUNT(*) FROM `products`
SQL
products 테이블 데이터 개수를 반환·
20/72
CRUD: Reading and Writing DataRead
# return the first productproduct = Product.first
RUBY
# => equivalent to:SELECT `products`.* FROM `products` ORDER BY `products`.`id` ASC LIMIT 1
SQL
products 테이블 내 첫 번째 데이터를 반환ORDER BY id ASC
··
21/72
CRUD: Reading and Writing DataRead
# return the last productproduct = Product.last
RUBY
# => equivalent to:SELECT `products`.* FROM `products` ORDER BY `products`.`id` DESC LIMIT 1
SQL
products 테이블 내 마지막 데이터를 반환ORDER BY id DESC
··
22/72
CRUD: Reading and Writing DataRead
# Using find methodproduct = Product.find(1)
RUBY
# => equivalent to:SELECT `products`.* FROM `products` WHERE `products`.`id` = 1 LIMIT 1
SQL
products 테이블 내 Primary Key 가 1인 레코드 불러오기·
23/72
CRUD: Reading and Writing DataRead
# using find_by_{column_name} methodiphone6 = Product.find_by_name('iPhone 6')
RUBY
# => equivalent to:SELECT `products`.* FROM `products` WHERE `products`.`name` = 'iPhone 6' LIMIT 1
SQL
name이 'iPhone 6'인 레코드 중첫 번째 것을 가져옴
··
24/72
CRUD: Reading and Writing DataRead
# using where and order clauseapple_products = Product.where(vender: 'Apple').order("name ASC")
RUBY
# => equivalent to:SELECT `products`.* FROM `products` WHERE `products`.`vender` = 'Apple' ORDER BY name ASC
SQL
vender가 'Apple'인 레코드 컬렉션을name 컬럼의 오름차순(ASC)으로 가져옴
··
25/72
Further Reading:
Active Record Query Interface
26/72
CRUD: Reading and Writing DataWait... We need price column on products table
ALTER TABLE products ADD COLUMN price int(11); SQL
27/72
CRUD: Reading and Writing DataUpdate
iphone6 = Product.find_by_name('iPhone 6')iphone6.price = 850000iphone6.save
RUBY
name이 'iPhone 6'인 첫 번째 레코드를 불러 와서price 컬럼을 850000으로 설정하고저장
···
28/72
CRUD: Reading and Writing DataUpdate
iphone6 = Product.find_by_name('iPhone 6')iphone6.update(price: 850000)
RUBY
update 메서드를 이용하는 방법·
29/72
CRUD: Reading and Writing DataBulk Update using update_all method
Product.update_all "price = price + 50000" RUBY
products 테이블 내 모든 레코드의 price를50000원 인상
··
30/72
CRUD: Reading and Writing DataDelete
product = Product.find_by_name('iPhone 6')product.destroy
RUBY
name이 'iPhone 6'인 첫 번째 레코드를 불러 와서삭제
··
31/72
Live Coding
32/72
Convention in Active RecordNaming Convention
Table (Database) - 언더스코어(_)로 단어 사이를 구분한 복수형Model (Class) - 각 단어의 첫 문자를 대문자로 표기한 단수형
··
33/72
Convention in Active RecordNaming Convention - By Example
Model / Class Table / Schema
Post posts
BookClub book_clubs
Deer deer
Mouse mice
Person people
34/72
Convention in Active RecordSchema Conventions
Column Name (Pattern) Use
id (by default) Primary Key
(singularized_table_name)_id Foreign Key
35/72
Convention in Active RecordSchema Conventions - Optional
Column Name (Pattern) Use
created_at Timestamp
updated_at Timestamp
lock_version Optimistic Locking
type Single Table Inheritance
(association_name)_type Polymorphic Association
(table_name)_count Cache the Number fo Belonging Objects on Associations
36/72
Convention in Active RecordUsing Timestamps
ALTER TABLE products ADD COLUMN created_at datetime;ALTER TABLE products ADD COLUMN updated_at datetime;
SQL
created_at : 레코드가 최초 생성될 때 현재 날짜와 시간을 설정updated_at : 레코드가 갱신될 때마다 현재 날짜와 시간을 설정
··
37/72
Overriding the Naming ConventionWhen
Need to Follow a Different Naming ConventionRails Application with a Legacy Database
··
38/72
Overriding the Naming ConventionExample
CREATE TABLE `book` ( `book_id` int(11) NOT NULL AUTO_INCREMENT, `title` varchar(255) DEFAULT NULL, `description` text, PRIMARY KEY (`book_id`))
SQL
39/72
Overriding the Naming ConventionSet Custom Table Name
class Book < ActiveRecord::Base self.table_name = "book"end
RUBY
use ActiveRecord::Base.table_name= method·
40/72
Overriding the Naming ConventionSet Custom Primary Key
class Book < ActiveRecord::Base self.table_name = "book" self.primary_key = "book_id"end
RUBY
use ActiveRecord::Base.primary_key= method·
41/72
Live Coding
42/72
MigrationDefinition
일관성 있고쉬운 방식으로데이터베이스를 변경할 수 있는편리한 방법
····
43/72
MigrationCreate New Model with Rails Command
# rails generate model {ModelName} [{column_name}:{type} [...]]rails generate model User name:string
TERMINAL
invoke active_record create db/migrate/20150131045821_create_users.rb create app/models/user.rb invoke test_unit create test/models/user_test.rb create test/fixtures/users.yml
OUTPUT
migrate 파일을 생성 (db/migrate 디렉터리)model 클래스를 생성 (app/model 디렉터리)테스트 유닛을 생성 (test 디렉터리)schema.rb 파일을 최신으로 갱신
····
44/72
MigrationMigration File (crete_table)
# in db/migrate/20150131045821_create_users.rbclass CreateUsers < ActiveRecord::Migration def change create_table :users do |t| t.string :name
t.timestamps end endend
RUBY
45/72
MigrationExecute Migration
rake db:migrate TERMINAL
== 20150131045821 CreateUsers: migrating ======================================-- create_table(:users) -> 0.0158s== 20150131045821 CreateUsers: migrated (0.0159s) =============================
OUTPUT
rake db:migrate현재까지 수행하지 않은 마이그레이션을 일괄 수행rake db:rollback으로 마이그레이션 수행 취소 가능schema_migrations 테이블 내에서 migration version을 관리
····
46/72
MigrationGenerate Migration
# rails generate migration {MigrationName} [{column_name}:{type} [...]]rails generate migration AddAgeAndJobToUsers age:integer job:string
TERMINAL
invoke active_record create db/migrate/20150131052039_add_age_and_job_to_users.rb
OUTPUT
migrate 파일을 생성 (db/migrate 디렉터리)·
47/72
MigrationMigration Naming Convention
source: Head First Rails
48/72
MigrationMigration File
# in db/migrate/20150131052039_add_age_and_job_to_users.rbclass AddAgeAndJobToUsers < ActiveRecord::Migration def change add_column :users, :age, :integer add_column :users, :job, :string endend
RUBY
49/72
MigrationExecute Migration
rake db:migrate TERMINAL
== 20150131052039 AddAgeAndJobToUsers: migrating ==============================-- add_column(:users, :age, :integer) -> 0.0220s-- add_column(:users, :job, :string) -> 0.0168s== 20150131052039 AddAgeAndJobToUsers: migrated (0.0389s) =====================
OUTPUT
50/72
MigrationWhenever you need to alter Database
migrate를 만들고rake db:migrate
··
51/72
참 쉽죠?
52/72
하나만 더 해 봅시다.
53/72
MigrationGenerate New Model with Relationship
rails generate model TodoItem user:references title:string memo:text TERMINAL
invoke active_record create db/migrate/20150131054248_create_todo_items.rb create app/models/todo_item.rb invoke test_unit create test/models/todo_item_test.rb create test/fixtures/todo_items.yml
OUTPUT
54/72
MigrationExecute Migration
rake db:migrate TERMINAL
== 20150131054248 CreateTodoItems: migrating ==================================-- create_table(:todo_items) -> 0.0444s== 20150131054248 CreateTodoItems: migrated (0.0445s) =========================
OUTPUT
55/72
MigrationModify Models
# in app/model/todo_item.rbclass TodoItem < ActiveRecord::Base belongs_to :userend
RUBY
# in app/model/user.rbclass User < ActiveRecord::Base has_many :todo_items, dependent: :destroyend
RUBY
User 모델과 TodoItem 간에 1:다 관계를 맺고User 모델이 삭제될 때, 연관된 TodoItem들도 삭제되도록 dependent를 지정
··
56/72
Live Coding
57/72
Further Reading:
Active Record Migrations
58/72
ValidationsWhat is Validations?
모델이 데이터베이스에 저장되기 전에모델 상태의 유효성을 검증
··
59/72
ValidationsSimple Example
# in app/model/user.rbclass User < ActiveRecord::Base validates :name, presence: trueend
RUBY
User.create # => falseUser.create! # => ActiveRecord::Record Invalid: Validation failed: Name can't be blank
RUBY
create, save, update 등 저장 명령이 수행될 때=> 유효성 검증이 실패하면 false를 반환, 실제 데이터베이스에 저장되지 않음Bang 메서드(create!, save!, update!)의 경우=> 보다 엄격한 검사를 수행: 실패시 ActiveRecord::RecordInvalid 예외를 일으킴
····
60/72
Validations언제 유효성을 검증하는가?
method ! method
create create!
save save!
update update!
61/72
ValidationsSkipping Validations (1)
method
decrement!
decrement_counter
increment!
increment_counter
toggle!
touch
increment!
62/72
ValidationsSkipping Validations (2)
method
update_all
update_attribute
update_column
update_columns
update_counters
63/72
ValidationsSkipping Validations (3)
User.save(validate: false) RUBY
save 메서드의 경우 validate: false와 함께 수행하면 검증 절차를 건너뜀매우 유의하여 사용하여야 함
··
64/72
Further Reading:
Active Record Validations
65/72
CallbacksWhat is Callbacks?
Active Record Object 생애 주기상의 특정 시점을 후킹해당 상태의 시점에서 응용프로그램 혹은 객체의 데이터를 제어
··
66/72
CallbacksCreating an Object
Available Callbacks
before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
after_save
67/72
CallbacksUpdating an Object
Available Callbacks
before_validation
after_validation
before_save
around_save
before_update
around_update
after_update
after_save
68/72
CallbacksDestroying an Object
Available Callbacks
before_destroy
around_destroy
after_destroy
69/72
CallbacksSequence of Active Record Callbacks
source: Active Record in Rails 4
70/72
Further Reading:
Active Record Callbacks
71/72
<Thank You!>To Be Continued...