update_attribute とupdate_attribtues の違い

以前に書いたコードを読んでいると、1カラムの値だけを変えるからといって、update_attribute メソッドを使っている部分があった。これはあまりよくない

update_attribute とupdate_attribtues の違い

前者は1つのカラムの値を、後者は複数のカラムの値を更新できる。という違い以上に、「update_attribute メソッドはvalidation なしに更新、update_attributes メソッドはvalidation ありで更新」という違いがあります。
つまり、update_attribute 使うと、検証処理なしに、値が更新されてしまう。ソースを見ると、update_attribute の方は、save(false) を使って更新しているのが分かる。

# Updates a single attribute and saves the record without going through the normal validation procedure.
# This is especially useful for boolean flags on existing records. The regular +update_attribute+ method
# in Base is replaced with this when the validations module is mixed in, which it is by default.
  def update_attribute(name, value)
    send(name.to_s + '=', value)
    save(false)
  end
 
# Updates all the attributes from the passed-in Hash and saves the record. If the object is invalid, the saving will
# fail and false will be returned.
  def update_attributes(attributes)
    self.attributes = attributes
    save
  end

update_attribute をこんな風に使うとやばい

  • 1カラムだけの更新だけど、値はクライアントから送られる
def update_age
  @user.update_attiribute(:age, params[:age])
end


params[:age] に「ほげほげ」とか送られてきても更新されてしまう。

  • テストケースの為に値を変更する場合
  user = users(:bob)
  user.update_attribute(:age, 23)


この場合も、不正な値を入れても更新されてしまいます。逆に、フィクスチャデータ自体がおかしい(AR#valid? がfalse を返す)というとんでもない状態になっててもスルーされて気がつかない。

update_attribute を使う際には注意

上のまずい例の場合、「save」メソッドを使うように書き換えた方がよいと思います。または、「update_attibutes」メソッドを使う。
モデルのメソッド等、ユーザからの値に依存しない変更を送る場合には使ってもいいかと思います。

def inactive!
  update_attibute(:active, false)
end