Ga Tech

1.01 everyday

Rails 4(2)

紀錄 Rails 3 to Rails 4 的一些改變。

FINDERS

在 Rails 3 當中,old-style finders 的寫法已經被摒棄了:
Post.find(:all, conditions: { author: ‘admin’ }) # deprecated

在 Rails 4 裡,比較正確的寫法應該是:
Post.where(author: ‘admin’)

FIND_BY

此外,Dynamic find_by finders 也被摒棄掉了:
Post.find_by_title(‘Rails 4’, conditions: { author: ‘admin’ }) # deprecated

在 Rails 4 裡,要寫成:
Post.find_by(title: ‘Rails 4’, author: ‘admin’)

或是寫成另一種比較好維護的寫法:
post_params = { title: ‘Rails 4’, author: ‘admin’ }
Post.find_by(post_params)

more about FIND_BY

更進一步查看 find_by 的實作方式:

/activerecord/lib/active_record/relation/finder_methods.rb
1
2
3
def find_by(*args)
where(*args).take
end

會發現 find_by 只是將 where method 給包起來而已。因此,find_by 也接受 where 的 arguents:
Post.find_by(“publicshed_on < ?”, 2.weeks.ago)

FINDOR*

同樣地,動態新增 objects 的 find_or_* 寫法也有改變:
Post.find_or_initialize_by_title(‘Rails 4’) # deprecated
Post.find_or_create_by_title(‘Rails 4’) # deprecated

在 Rails 4 當中,要改為:
Post.find_or_initialize_by(title: ‘Rails 4’)
Post.find_or_create_by(title: ‘Rails 4’)

more about FINDOR*

動態新增 objects 還有另一種寫法:
Post.where(title: ‘Rails 4’).find_or_create

意思就是,如果找不到這個 Post,就會新增一個:
Post.where(title: ‘Rails 4’).create

但是並不建議這種寫法,因為如果有針對 create 寫了任何 callback 的話,就會導致一連串不必要的 query 產生。
比如:

/model/post.rb
1
2
3
4
5
6
7
8
class Post < ActiveRecord::Base
after_create :foo
def foo
posts = Post.where(author: 'adin')
...
end
end

會產生這樣的 query:
SELECT “posts”.* FROM “posts” WHERE “posts”.”title” = ‘Rails 4’ AND “posts”.”author” = ‘admin’…

明顯不是我們想要的。

基於上述原因,Rails 4 才會使用 find_or_create_by
Post.find_or_create_by(title: ‘Rails 4’)

這樣一來,如果找不到這個 Post,就只會新增一個:
Post.create(title: ‘Rails 4’)

產生的 query 會乾淨得多:
SELECT “posts”.* FROM “posts” WHERE “posts”.”author” = ‘admin’

如果仍然想使用舊的 finders 寫法也不用擔心,這些寫法都包在 activerecord-deprecated_finders 這個 gem 裡頭了。

UPDATE

在 Rails 3 當中,通常會使用下列的方法來進行 update:
@post.update_attributes(post_params)

@post.update_attribute(:title, 'Rails 4') # might be deprecated in the future
@post.update_column(:title, 'Rails 4') # might be deprecated in the future

主要差別在於下面兩個在 update 的過程不會進行 validate。

而在 Rails 4 裡,比較恰當的寫法是:
@post.update(post_params)

@post.update_columns(post_params)

這兩個的差別也是在於 update_columns 會直接產生 SQL statement 並對 database 執行,也就是會 skip validation。

MODEL.ALL

在 Rails 3 當中,通常在 index action 裡面會使用 scoped 先產生一個 active relation object,然後再 chain 其他的 method:

controllers/tweets_controller.rb
1
2
3
4
5
6
def index
@tweets = Tweet.scoped
if params[:recent]
@tweets = @tweets.recent # dynamic conditions
end
end

但在 Rails 4 裡,scoped 已經被摒棄掉,而改用 all 了:

controllers/tweets_controller.rb
1
2
3
4
5
6
def index
@tweets = Tweet.all #chainable
if params[:recent]
@tweets = @tweets.recent # dynamic conditions
end
end

source: CodeSchool

Comments