Rails5中AR的新特性

Rails5正式版, 終于經過眾多測試版后痊末,與我們見面了振惰,本文就來介紹一下这弧,在Rails5中有哪些關于ActiveRecord相關的新特性捷凄,以便能夠更好的使用Rails5進行開發(fā)忱详。

ApplicationRecord


在Rails4中所有的模型都繼承自ActiveRecord::Base,不過在Rails5中新引進了一個叫ApplicationRecord的類跺涤,存放在: app/models/application_record.rb中匈睁,所有Rails5應用都會有這個類, 它的內容非常簡單:

class ApplicationRecord < ActiveRecord::Base  
  self.abstract_class = true
end

就是一個繼承ActiveRecord::Base的抽象類监透,作用就是為整個應用程序模型提供一個自己的基類

module MyModule
end
# Rails4.x中擴展模型的方式
ActiveRecord::Base.send :include, MyModule
# Rails5
class ApplicationRecord < ActiveRecord::Base
  include MyModule
  self.abstract_class = true
end

OR語法支持


Rails5中提供了對兩個AR Relation對象的OR方法:

 > Article.where(user_id: 1).or(Article.where(user_id: 2))
=> Article Load (2.5ms)  SELECT `articles`.* FROM `articles` WHERE (`articles`.`user_id` = 1 OR `articles`.`user_id` = 2)

需要注意的是如果你在第一個Relation中是用了:limit distinct offset 這三個方法的話,那么就必須在后面的Relation中也使用相同的方法航唆,否則的話就會報錯

> Article.where(user_id: 1).limit(1).or(Article.where(user_id: 2))
#=>ArgumentError: Relation passed to #or must be structurally compatible. Incompatible values: [:limit]

最好是在結尾使用:

Article.where(user_id: 1).or(Article.where(user_id: 2)).limit(1)

ActiveRecord::Relation#cache_key


Rails中使用緩存是很常見的行為胀蛮,通常我們要緩存一組查詢出來的記錄,需要手動的設置緩存的key

no_nick_name_users = User.where(nick_name: nil)
cache_key = [User.name, 'no_nick_name_users', no_nick_name_users.maximum(:updated_at).to_i]
Rails.cache.fetch(cache_key) do
  no_nick_name_users.to_a
end

Rails5中提供了ActiveRecord::Relation#cache_key

no_nick_name_users = User.where(nick_name: nil)
Rails.cache.fetch(no_nick_name_users.cache_key) do
  no_nick_name_users.to_a
end
puts no_nick_name_users.cache_key
#=> "users/query-dae9b6f1d9babd4a9ec4c532614c29eb-1-20160703095605000000"

上面最后一行佛点,Rails5提供的cache_key和我們自己設置的很相似醇滥,分別有5個組成部分分別是:

  • users : 表名
  • query : 常值
  • dae9b6f1d9babd4a9ec4c532614c29eb : 緩存SQL的MD5碼
  • 1 : 結果集數量
  • 20160703095605000000 : 結果集最大的updated_at的時間戳

AR Relation調用update會觸發(fā)callbacks和validates


在Rails4中的AR Relation 提供了兩個更新記錄的方法update_allupdate其中:

  • update_all 通過一條SQL語句更新多條記錄黎比,不能觸發(fā)callback和validate
  • update 通過N條SQL語句超营,更新N條記錄,其中N取決于其第一個ID參數的個數阅虫。

通過上面的方法定義演闭,可以看出,如果你不知道ID的情況下颓帝,想更新一組記錄并且觸發(fā)它們各自的callback和validate米碰,在Rails4中是做不到的。
那么在Rails5中修改了AR Relation#update的實現:

def update(id = :all, attributes)
   if id.is_a?(Array)
     id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }
   elsif id == :all
     to_a.each { |record| record.update(attributes) }
   else
     object = find(id)
     object.update(attributes)
    object
  end
end

也就是亦可以通過下面的方法更新記錄:

2.3.0 :007 > User.where(nick_name: 'Falm').update(nick_name: 'falm')
  User Load (0.3ms)  SELECT `users`.* FROM `users` WHERE `users`.`nick_name` = 'Falm'
   (0.1ms)  BEGIN
  SQL (0.3ms)  UPDATE `users` SET `nick_name` = 'falm', `updated_at` = '2016-07-03 05:00:18' WHERE `users`.`id` = 1
   (2.0ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.2ms)  UPDATE `users` SET `nick_name` = 'falm', `updated_at` = '2016-07-03 05:00:18' WHERE `users`.`id` = 2
   (0.3ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.2ms)  UPDATE `users` SET `nick_name` = 'falm', `updated_at` = '2016-07-03 05:00:18' WHERE `users`.`id` = 3
   (0.2ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.2ms)  UPDATE `users` SET `nick_name` = 'falm', `updated_at` = '2016-07-03 05:00:18' WHERE `users`.`id` = 4
   (0.2ms)  COMMIT

更新操作被按ID分解成多個update語句购城,并且其中每一個都會執(zhí)行callback和validates, 要注意的是如果你要更新的記錄不必要觸發(fā)callback或validates吕座,那么因為性能原因最好使用update_all方法。

更新記錄時瘪板,不更新updated_at/updated_on


Rails4.x中吴趴,更新記錄是,AR都會連帶更新侮攀,記錄上的updated_atupdated_on字段锣枝。
在Rails5中,為ActiveRecord::Base#save方法提供了一個選項兰英,touch: boolean撇叁,默認情況下是true,如果設置成false的話畦贸,更新記錄是就不會更新updated_at字段了陨闹。

2.3.0 :027 > user = User.first
  User Load (0.3ms)  SELECT  `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
 => #<User id: 1, phone: "13303300333", email: nil, sign_in_count: 0, name: "Jason", nick_name: "falm", encrypted_password: nil, created_at: "2016-07-03 05:00:14", updated_at: "2016-07-03 05:00:18">
2.3.0 :028 > user.phone = '15088833388'
 => "15088833388"
2.3.0 :029 > user.save(touch: false)
   (0.2ms)  BEGIN
  SQL (0.3ms)  UPDATE `users` SET `phone` = '15088833388' WHERE `users`.`id` = 1
   (0.4ms)  COMMIT
 => true
2.3.0 :030 > user.updated_at
 => Sun, 03 Jul 2016 05:00:18 UTC +00:00

忽略字段


Rails5中新增了 ActiveRecord::Base.ignored_columns 方法,用于忽略數據表中不需要的字段薄坏。

class User < ApplicationRecord
  self.ignored_columns = ['sign_in_count']
end

這樣在模型中就不會有這個字段了

2.3.0 :033 > User.first
  User Load (0.2ms)  SELECT  `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
 => #<User id: 1, phone: "15088833388", email: nil, name: "Jason", nick_name: "falm", encrypted_password: nil, created_at: "2016-07-03 05:00:14", updated_at: "2016-07-03 05:00:18">

Belongs_to關聯趋厉,默認必填


在Rails5中AR中的belongs_to 關聯,默認情況下是不能為空的:

class User < ApplicationRecord
end
class Article < ApplicationRecord  
  belongs_to :user
end
> Article.create(title: 'without user').errors.full_messages.to_sentence
   (0.2ms)  BEGIN
   (0.1ms)  ROLLBACK
 => "User must exist"

Article屬于User颤殴,但是如果沒有在創(chuàng)建時指定user的話觅廓,就無法通過AR的validates,如果你想去除這個默認選項的話涵但,可以通過下面的方式:

class Article < ApplicationRecord  
  belongs_to :user, optional: true, #指定可選
end

也可以在application.rb中全局設置這個特性為可選的杈绸。

Rails.application.config.active_record.belongs_to_required_by_default = false

新的 after_{create,update,delete}_commit 回調


在Rails4中帖蔓,我們可以在模型中設置事務執(zhí)行后的回調方法,

# == Schema Information
#
# Table name: users
#
#  id                 :integer          not null, primary key
#  phone              :string(255)      not null
#  email              :string(255)
#  sign_in_count      :integer          default(0)
#  name               :string(255)      not null
#  nick_name          :string(255)
#  encrypted_password :string(255)
#  created_at         :datetime         not null
#  updated_at         :datetime         not null
#
class User < ApplicationRecord
  after_commit :send_message, on: :create
  after_commit :send_message, on: :update
  after_commit :send_message, on: :destroy
  private
  def send_message
    do_someting
  end
end

以上就是分別在 創(chuàng)建瞳脓,更新塑娇,和刪除事務執(zhí)行后,被調用的回調方法
那么在Rails5中給它們分別提供的單獨的別名方法:

class User < ApplicationRecord
  after_create_commit :send_message
  after_update_commit :send_message
  after_destroy_commit :send_message
  private
  def send_message
    do_someting
  end
end

支持在migration中添加comments


在大多數的項目中劫侧,快速變化的業(yè)務模型是很常見的事情埋酬,隨之而來的就是數據模型的頻繁變化,在這種場景下烧栋,我們通常會使用 migration_comments + annotate_models 這兩個gem 去為模型添加注釋信息写妥,以便能夠更好的解釋模型的由來和用途。現在Rails5原生支持了 migration_comments的功能:

#encoding: utf-8
class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users, comment: '用戶表' do |t|
      t.string :phone, null: false, comment: '手機號'
      t.string :email, comment: '郵箱'
      t.integer :sign_in_count, default: 0, comment: '登錄次數'
      t.string :name, null: false, comment: '用戶名'
      t.string :nick_name, comment: '昵稱'
      t.string :encrypted_password, comment: '加密密碼'
      t.timestamps
    end
  end
end

運行遷移:

$> rails db:migrate
== 20160703025729 CreateUsers: migrating ======================================
-- create_table(:users, {:comment=>"用戶表"})
   -> 0.0178s
== 20160703025729 CreateUsers: migrated (0.0179s) =============================

在mysql數據庫中也可以查看到注釋:

mysql> show full columns from users;
+--------------------+--------------+-----------------+--------+-------+-----------+----------------+---------------------------------+-----------+
| Field              | Type         | Collation       | Null   | Key   |   Default | Extra          | Privileges                      | Comment   |
|--------------------+--------------+-----------------+--------+-------+-----------+----------------+---------------------------------+-----------|
| id                 | int(11)      | <null>          | NO     | PRI   |    <null> | auto_increment | select,insert,update,references |           |
| phone              | varchar(255) | utf8_general_ci | NO     |       |    <null> |                | select,insert,update,references | 手機號    |
| email              | varchar(255) | utf8_general_ci | YES    |       |    <null> |                | select,insert,update,references | 郵箱      |
| sign_in_count      | int(11)      | <null>          | YES    |       |         0 |                | select,insert,update,references | 登錄次數  |
| name               | varchar(255) | utf8_general_ci | NO     |       |    <null> |                | select,insert,update,references | 用戶名    |
| nick_name          | varchar(255) | utf8_general_ci | YES    |       |    <null> |                | select,insert,update,references | 昵稱      |
| encrypted_password | varchar(255) | utf8_general_ci | YES    |       |    <null> |                | select,insert,update,references | 加密密碼  |
| created_at         | datetime     | <null>          | NO     |       |    <null> |                | select,insert,update,references |           |
| updated_at         | datetime     | <null>          | NO     |       |    <null> |                | select,insert,update,references |           |
+--------------------+--------------+-----------------+--------+-------+-----------+----------------+---------------------------------+-----------+
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末审姓,一起剝皮案震驚了整個濱河市珍特,隨后出現的幾起案子,更是在濱河造成了極大的恐慌魔吐,老刑警劉巖扎筒,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異酬姆,居然都是意外死亡嗜桌,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門辞色,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骨宠,“玉大人,你說我怎么就攤上這事淫僻∮张瘢” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵雳灵,是天一觀的道長棕所。 經常有香客問我,道長悯辙,這世上最難降的妖魔是什么琳省? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮躲撰,結果婚禮上针贬,老公的妹妹穿的比我還像新娘。我一直安慰自己拢蛋,他們只是感情好桦他,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谆棱,像睡著了一般快压。 火紅的嫁衣襯著肌膚如雪圆仔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天蔫劣,我揣著相機與錄音坪郭,去河邊找鬼。 笑死脉幢,一個胖子當著我的面吹牛歪沃,可吹牛的內容都是我干的。 我是一名探鬼主播嫌松,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼沪曙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了豆瘫?” 一聲冷哼從身側響起珊蟀,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎外驱,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體腻窒,經...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡昵宇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了儿子。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓦哎。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖柔逼,靈堂內的尸體忽然破棺而出蒋譬,到底是詐尸還是另有隱情,我是刑警寧澤愉适,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布犯助,位于F島的核電站,受9級特大地震影響维咸,放射性物質發(fā)生泄漏剂买。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一癌蓖、第九天 我趴在偏房一處隱蔽的房頂上張望瞬哼。 院中可真熱鬧,春花似錦租副、人聲如沸坐慰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽结胀。三九已至两残,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間把跨,已是汗流浹背人弓。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留着逐,地道東北人崔赌。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像耸别,于是被迫代替她去往敵國和親健芭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內容