Rails 入門教程

模型

一、Active Record 基礎(chǔ)
介紹Models,數(shù)據(jù)庫持久性以及Active Record模式

  1. Active Record是什么?
    Active Record 是 MVC 中的 M(模型),負責處理數(shù)據(jù)和業(yè)務邏輯。
    注意:這里業(yè)務邏輯在Model處理。
    Active Record負責創(chuàng)建和使用需要持久存入數(shù)據(jù)庫的數(shù)據(jù)。Active Record實現(xiàn)了Active Record模式,是一種對象關(guān)系映射系統(tǒng)。

1-1)Active Record模式
在Active Record模式中,對象中既有持久存儲的數(shù)據(jù),也有針對數(shù)據(jù)庫的操作。Active Record模式吧數(shù)據(jù)存取邏輯作為對象的一部分,處理對象的用戶知道如何把數(shù)據(jù)寫入數(shù)據(jù)庫,還知道如何從數(shù)據(jù)庫取出數(shù)據(jù)。

1-2)對象關(guān)系映射
對象關(guān)系映射(ORM)Object-Relational-Mapping
是一種技術(shù)手段,吧應用中的對象和關(guān)系型數(shù)據(jù)庫中的數(shù)據(jù)表連接起來。使用ORM,應用中對象的屬性和對象之間的關(guān)系可以通過一種簡單的方法從數(shù)據(jù)庫中獲取,無需直接編寫SQL語句,也不過度依賴特定的數(shù)據(jù)庫種類。

1-3)用作ORM框架的Active Record
Active Record作用:

  • 表示模型和其中的數(shù)據(jù)
  • 表示模型之間的關(guān)系
  • 通過相關(guān)聯(lián)的模型表示繼承層次結(jié)構(gòu)
  • 持久存入數(shù)據(jù)庫之前,驗證模型
  • 以面向?qū)ο蟮姆绞教幚頂?shù)據(jù)庫操作

2)Active Record中的約定大于配置原則

使用其他編程語言或框架開發(fā)應用時,可能必須要編寫很多配置代碼。大多數(shù) ORM 框架都是這樣。但是,如果遵循 Rails 的約定,創(chuàng)建 Active Record 模型時不用做多少配置(有時甚至完全不用配置)。Rails 的理念是,如果大多數(shù)情況下都要使用相同的方式配置應用,那么就應該把這定為默認的方式。所以,只有約定無法滿足要求時,才要額外配置。

2-1)命名約定
默認情況下,Active Record使用一些命名約定,查找模型和數(shù)據(jù)庫表之間的映射關(guān)系。Rails吧模型的類名轉(zhuǎn)換為復數(shù),然后查找對應的數(shù)據(jù)表。
eg:

模型類名為Book, 數(shù)據(jù)庫表 books

Rails提供的單復數(shù)轉(zhuǎn)換功能很強大,常見和不常見的轉(zhuǎn)換方式都能處理。

模型類:單數(shù),每個單詞的首寫字母應該大寫 BookClub
數(shù)據(jù)庫表: 復數(shù),下劃線分隔單詞 book_clubs
圖片.png

2-2)模式約定
根據(jù)字段的作用不同,Active Record對數(shù)據(jù)庫表中的字段命名也做了相應的約定:

  • 外鍵:使用 singularized_table_name_id 形式命名,例如 item_id,order_id。創(chuàng)建模型關(guān)聯(lián)后,Active Record 會查找這個字段;

  • 主鍵:默認情況下,Active Record使用整數(shù)字段id作為表的主鍵。使用Active Record遷移創(chuàng)建數(shù)據(jù)庫表時,會自動創(chuàng)建這個字段。

還有一些可選的字段,能為 Active Record 實例添加更多的功能:

  • created_at:創(chuàng)建記錄時,自動設為當前的日期和時間;

  • updated_at:更新記錄時,自動設為當前的日期和時間;

  • lock_version:在模型中添加樂觀鎖;

  • type:讓模型使用單表繼承;

  • (association_name)_type:存儲多態(tài)關(guān)聯(lián)的類型;

  • (table_name)_count:緩存所關(guān)聯(lián)對象的數(shù)量。比如說,一個 Article 有多個 Comment,那么 comments_count 列存儲各篇文章現(xiàn)有的評論數(shù)量;

雖然這些字段是可選的,但在 Active Record 中是被保留的。如果想使用相應的功能,就不要把這些保留字段用作其他用途。例如,type 這個保留字段是用來指定數(shù)據(jù)庫表使用單表繼承(Single Table Inheritance,STI)的。如果不用單表繼承,請使用其他的名稱,例如“context”,這也能表明數(shù)據(jù)的作用。

3)創(chuàng)建Active Record模型

創(chuàng)建 Active Record 模型的過程很簡單,只要繼承 ApplicationRecord 類就行了:

class Product < ApplicationRecord

end

這代碼會創(chuàng)建Product模型,對應于數(shù)據(jù)庫中的products表。
上面的代碼會創(chuàng)建 Product 模型,對應于數(shù)據(jù)庫中的 products 表。同時,products 表中的字段也映射到 Product 模型實例的屬性上。假如 products 表由下面的 SQL 語句創(chuàng)建:

CREATE TABLE products (
   id int(11) NOT NULL auto_increment,
   name varchar(255),
   PRIMARY KEY  (id)
);

按照這樣的數(shù)據(jù)表結(jié)構(gòu),可以編寫下面的代碼:

p = Product.new
p.name = "Some Book"
puts p.name # "Some Book"

4)覆蓋命名約定

如果想使用其他的命名約定,或者在Rails應用張使用即有的數(shù)據(jù)庫可以嗎?沒問題,默認的約定能夠輕易覆蓋。

ApplicationRecord 繼承自 ActiveRecord::Base,后者定義了一系列有用的方法。使用 ActiveRecord::Base.table_name= 方法可以指定要使用的表名:

class Product < ApplicationRecord
  self.table_name = "my_products"
end

如果這么做,還要調(diào)用 set_fixture_class 方法,手動指定固件(my_products.yml)的類名:

class ProductTest < ActiveSupport::TestCase
  set_fixture_class my_products: Product
  fixtures :my_products
  ...
end

還可以使用 ActiveRecord::Base.primary_key= 方法指定表的主鍵:

class Product < ApplicationRecord
  self.primary_key = "product_id"
end

5)CRUD:讀寫數(shù)據(jù)

CURD 是四種數(shù)據(jù)操作的簡稱:C 表示創(chuàng)建,R 表示讀取,U 表示更新,D 表示刪除。Active Record 自動創(chuàng)建了處理數(shù)據(jù)表中數(shù)據(jù)的方法。增刪改查。

5-1)create增
Active Record 對象可以使用散列創(chuàng)建,在塊中創(chuàng)建,或者創(chuàng)建后手動設置屬性。new 方法創(chuàng)建一個新對象,create 方法創(chuàng)建新對象,并將其存入數(shù)據(jù)庫。
例如,User 模型中有兩個屬性,name 和 occupation。調(diào)用 create 方法會創(chuàng)建一個新記錄,并將其存入數(shù)據(jù)庫:

user = User.create(name: "David", occupation: "Code Artist")

new 方法實例化一個新對象,但不保存:

user = User.new
user.name = "David"
user.occupation = "Code Artist"

調(diào)用 user.save 可以把記錄存入數(shù)據(jù)庫。

如果在 create 和 new 方法中使用塊,會把新創(chuàng)建的對象拉入塊中,初始化對象:

user = User.new do |u|
  u.name = "David"
  u.occupation = "Code Artist"
end

5-2)query查
Active Record 為讀取數(shù)據(jù)庫中的數(shù)據(jù)提供了豐富的 API。
返回所有集合:

users=User.all

返回第一個:

user=User.first

返回第一個名為David用戶:

davidUser = User.find_by(name:'David')

查找所有名字為David,職業(yè)為Code Artists的用戶,而且按照created_at 反向排序:

users=User.find_by(name:'David', occupation:'Code Artists').order(created_at::desc)

5-3)Update更新

檢索到Active Record對象后,可以修改其屬性,然后再將其存入數(shù)據(jù)庫。

user=User.find_by(name:'David')
user.name=‘Dave’
user.save

還有種使用散列的簡寫方式,指定屬性名和屬性值,例如:

user=User.find_by(name:'David')
user.update(name:'Dave')

一次更新多個屬性時使用這種方法最方便。如果想批量更新多個記錄,可以使用類方法update_all:

User.update_all "max_login_attempts=3, must_change_password='true'"

5-4)Delete刪除

檢索到Active Record對象后還可以將其銷毀,從數(shù)據(jù)庫中刪除。

user=User.find_by(name:'David')
user.destroy

6)數(shù)據(jù)驗證

在存入數(shù)據(jù)庫之前,Active Record還可以驗證模型。模型驗證有很多方法,可以檢查屬性值是否不為空,是否是唯一,沒有在數(shù)據(jù)庫中出現(xiàn)過,等等。

吧數(shù)據(jù)存入數(shù)據(jù)庫之前進行驗證是是否重要的步驟,所以調(diào)用save和update方法時都會做數(shù)據(jù)驗證。驗證失敗時返回false,此時不會對數(shù)據(jù)庫做任何操作。這2個方法都有對應的爆炸方法(save!和update!)。爆炸方法要嚴格一些,如果驗證失敗,就會拋出ActiveRecord::Recordnvalid異常。

class User < ApplicationRecord
  validates :name, presence: true
end
 
user = User.new
user.save  # => false
user.save! # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank

注意:驗證是在寫class的時候,預定了validates

7)回調(diào)
Active Record回調(diào)用于在模型生命周期的特定時間上綁定代碼,相應的事件發(fā)生時,執(zhí)行綁定的代碼。例如創(chuàng)建新記錄,更新記錄時,刪除記錄時,等等。

8)遷移
Rails提供了一個DSL(Domain-Special Language)用來處理數(shù)據(jù)庫模式,叫作”遷移“。
注意:什么是數(shù)據(jù)庫模式?
模式(schema)是數(shù)據(jù)庫體系結(jié)構(gòu)中的一個節(jié)點
對于SQL Server數(shù)據(jù)庫來說:
訪問一個具體的表,可以由4個部分組成:

服務器名+數(shù)據(jù)庫名+模式名+表名

對于訪問本地的數(shù)據(jù)庫:

因為服務器已經(jīng)連接上了,因此不用指定
數(shù)據(jù)庫名,通過use 數(shù)據(jù)庫名 指定了
模式名,如果不指定的話,數(shù)據(jù)庫默認使用dbo模式

模式(schema) 是用于 在一個 大項目中的 各個 小項目
每個 小項目的表, 放在 各自的 模式(schema) 下面.
這樣, 遇到 小項目里面. 有 相同名字的 表的話, 不會發(fā)生沖突.

例如一個 公司的 系統(tǒng).
里面分2個 子系統(tǒng), 分別為 財務系統(tǒng)人力資源系統(tǒng).
這2個 子系統(tǒng), 共用一個數(shù)據(jù)庫
那么 財務系統(tǒng)的表, 可以放在 財務的 模式(schema).
人力資源系統(tǒng)的表,放在 人力資源系統(tǒng)的模式里面。
這2個 子系統(tǒng), 能夠 互相訪問 對方的表
但是又不因為 表重名 的問題,影響對方。

遷移的代碼存儲在特定的文件中,通過 rails 命令執(zhí)行。可以用在 Active Record 支持的所有數(shù)據(jù)庫上。下面這個遷移新建一個表:

class CreatePublications < ActiveRecord::Migration[5.0]
  def change
    create_table :publications do |t|
      t.string :title
      t.text :description
      t.references :publication_type
      t.integer :publisher_id
      t.string :publisher_type
      t.boolean :single_issue
 
      t.timestamps
    end
    add_index :publications, :publication_type_id
  end
end

Rails 會跟蹤哪些遷移已經(jīng)應用到數(shù)據(jù)庫上,還提供了回滾功能。為了創(chuàng)建表,要執(zhí)行 rails db:migrate 命令。如果想回滾,則執(zhí)行 rails db:rollback 命令。

注意,上面的代碼與具體的數(shù)據(jù)庫種類無關(guān),可用于 MySQL、PostgreSQL、Oracle 等數(shù)據(jù)庫。

二、Active Record 數(shù)據(jù)庫遷移

遷移是 Active Record 的一個特性,允許我們按時間順序管理數(shù)據(jù)庫模式。有了遷移,就不必再用純 SQL 來修改數(shù)據(jù)庫模式,而是可以使用簡單的 Ruby DSL 來描述對數(shù)據(jù)表的修改。

1)遷移概述
遷移是以一致和輕松的方式按時間順序修改數(shù)據(jù)庫模式的實用方法。它使用 Ruby DSL,因此不必手動編寫 SQL,從而實現(xiàn)了數(shù)據(jù)庫無關(guān)的數(shù)據(jù)庫模式的創(chuàng)建和修改。

我們可以把遷移看做數(shù)據(jù)庫的新“版本”。數(shù)據(jù)庫模式一開始并不包含任何內(nèi)容,之后通過一個個遷移來添加或刪除數(shù)據(jù)表、字段和記錄。Active Record 知道如何沿著時間線更新數(shù)據(jù)庫模式,使其從任何歷史版本更新為最新版本。Active Record 還會更新 db/schema.rb文件,以匹配最新的數(shù)據(jù)庫結(jié)構(gòu)。

示例:

class CreateProducts < ActiveRecord::Migration[5.0]
  def change
    create_table :products do |t|
      t.string :name
      t.text :description
 
      t.timestamps
    end
  end
end

這個遷移用于添加 products 數(shù)據(jù)表,數(shù)據(jù)表中包含 name 字符串字段和 description 文本字段。同時隱式添加了 id 主鍵字段,這是所有 Active Record 模型的默認主鍵。timestamps 宏添加了 created_at 和 updated_at 兩個字段。后面這幾個特殊字段只要存在就都由 Active Record 自動管理。

對于支持事務并提供了用于修改數(shù)據(jù)庫模式的語句的數(shù)據(jù)庫,遷移被包裝在事務中。如果數(shù)據(jù)庫不支持事務,那么當遷移失敗時,已成功的那部分操作將無法回滾。這種情況下只能手動完成相應的回滾操作。

某些查詢不能在事務內(nèi)部運行。如果數(shù)據(jù)庫適配器支持 DDL 事務,就可以使用 disable_ddl_transaction! 方法在某個遷移中臨時禁用事務。
如果想在遷移中完成一些 Active Record 不知如何撤銷的操作,可以使用 reversible 方法:

class ChangeProductsPrice < ActiveRecord::Migration[5.0]
  def change
    reversible do |dir|
      change_table :products do |t|
        dir.up   { t.change :price, :string }
        dir.down { t.change :price, :integer }
      end
    end
  end
end

或者用 up 和 down 方法來代替 change 方法:


class ChangeProductsPrice < ActiveRecord::Migration[5.0]
  def up
    change_table :products do |t|
      t.change :price, :string
    end
  end
 
  def down
    change_table :products do |t|
      t.change :price, :integer
    end
  end
end

2)創(chuàng)建遷移

2-1)創(chuàng)建獨立的遷移
遷移文件儲存在 db/migrate 文件夾中,一個遷移文件包含一個遷移類。文件名采用 YYYYMMDDHHMMSS_create_products.rb 形式,即 UTC 時間戳加上下劃線再加上遷移的名稱。遷移類的名稱(駝峰式)應該匹配文件名中遷移的名稱。例如,在 20080906120000_create_products.rb 文件中應該定義 CreateProducts 類,在 20080906120001_add_details_to_products.rb 文件中應該定義 AddDetailsToProducts 類。Rails 根據(jù)文件名的時間戳部分確定要運行的遷移和遷移運行的順序,因此當需要把遷移文件復制到其他 Rails 應用,或者自己生成遷移文件時,一定要注意遷移運行的順序。

當然,計算時間戳不是什么有趣的事,因此 Active Record 提供了生成器:

$ bin/rails generate migration AddPartNumberToProducts

上面的命令會創(chuàng)建空的遷移,并進行適當命名:

class AddPartNumberToProducts < ActiveRecord::Migration[5.0]
  def change
  end
end

2-2)模型生成器
模型和腳手架生成器會生成適用于添加新模型的遷移。這些遷移中已經(jīng)包含用于創(chuàng)建有關(guān)數(shù)據(jù)表的指令。如果我們告訴 Rails 想要哪些字段,那么添加這些字段所需的語句也會被創(chuàng)建。例如,運行下面的命令:

$ bin/rails generate model Product name:string description:text

上面的命令會創(chuàng)建下面的遷移:

class CreateProducts < ActiveRecord::Migration[5.0]
  def change
    create_table :products do |t|
      t.string :name
      t.text :description
 
      t.timestamps
    end
  end
end

我們可以根據(jù)需要添加“字段名稱/類型”對,沒有數(shù)量限制。

2-3)傳遞修飾符

可以直接在命令行中傳遞常用的類型修飾符。這些類型修飾符用大括號括起來,放在字段類型之后。例如,運行下面的命令:

$ bin/rails generate migration AddDetailsToProducts 'price:decimal{5,2}' supplier:references{polymorphic}

上面的命令會創(chuàng)建下面的遷移:

class AddDetailsToProducts < ActiveRecord::Migration[5.0]
  def change
    add_column :products, :price, :decimal, precision: 5, scale: 2
    add_reference :products, :supplier, polymorphic: true, index: true
  end
end

3)編寫遷移

使用生成器創(chuàng)建遷移后,就可以開始寫代碼了。

3-1)創(chuàng)建數(shù)據(jù)表

create_table方法是最基礎(chǔ)、最常用的方法,其代碼通常是由模型或腳手架生成器生成的。典型的用法像下面這樣:

create_table :products do |t|
  t.string :name
end

上面的命令會創(chuàng)建包含 name 字段的 products 數(shù)據(jù)表(后面會介紹,數(shù)據(jù)表還包含自動創(chuàng)建的 id 字段)。
默認情況下,create_table 方法會創(chuàng)建 id 主鍵。可以用 :primary_key 選項來修改主鍵名稱,還可以傳入 id: false 選項以禁用主鍵。如果需要傳遞數(shù)據(jù)庫特有的選項,可以在 :options 選項中使用 SQL 代碼片段。例如:

create_table :products, options: "ENGINE=BLACKHOLE" do |t|
  t.string :name, null: false
end

上面的代碼會在用于創(chuàng)建數(shù)據(jù)表的 SQL 語句末尾加上 ENGINE=BLACKHOLE(如果使用 MySQL 或 MarialDB,默認選項是 ENGINE=InnoDB)。
還可以傳遞帶有數(shù)據(jù)表描述信息的 :comment 選項,這些注釋會被儲存在數(shù)據(jù)庫中,可以使用 MySQL Workbench、PgAdmin III 等數(shù)據(jù)庫管理工具查看。對于大型數(shù)據(jù)庫,強列推薦在應用的遷移中添加注釋。目前只有 MySQL 和 PostgreSQL 適配器支持注釋功能。
3-2)創(chuàng)建聯(lián)結(jié)數(shù)據(jù)表
create_join_table 方法用于創(chuàng)建 HABTM(has and belongs to many)聯(lián)結(jié)數(shù)據(jù)表。典型的用法像下面這樣:

create_join_table :products, :categories

上面的代碼會創(chuàng)建包含 category_id 和 product_id 字段的 categories_products 數(shù)據(jù)表。這兩個字段的 :null 選項默認設置為 false,可以通過 :column_options 選項覆蓋這一設置:

create_join_table :products, :categories, column_options: { null: true }

聯(lián)結(jié)數(shù)據(jù)表的名稱默認由 create_join_table 方法的前兩個參數(shù)按字母順序組合而來。可以傳入 :table_name 選項來自定義聯(lián)結(jié)數(shù)據(jù)表的名稱:


create_join_table :products, :categories, table_name: :categorization

上面的代碼會創(chuàng)建 categorization 數(shù)據(jù)表。
create_join_table 方法也接受塊作為參數(shù),用于添加索引(默認未創(chuàng)建的索引)或附加字段:

create_join_table :products, :categories do |t|
  t.index :product_id
  t.index :category_id
end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,743評論 2 370

推薦閱讀更多精彩內(nèi)容