Different Ways to Set Attributes in ActiveRecord (Rails 4)

Khi bắt đầu học Ruby on Rails, mình thấy có rất nhiều cách để thay đổi các attributes của một object và không biết khi nào nên dùng cách nào. Tuy nhiên sau quá trình tìm hiểu, mình đã tìm được một bài viết rất hay, có tổng hợp đầy đủ các cách để thay đổi thuộc tính trong Rails và sự khác nhau giữa chúng, bài viết đã giúp mình rất nhiều. Sau đây mình xin giới thiệu nội dung của bài viết đó, hi vọng nó sẽ giúp các bạn sử dụng Active Record hiệu quả hơn.

Nguồn: http://www.davidverhasselt.com/set-attributes-in-activerecord/

Trong nội dung bài viết chúng ta sẽ xét một đối tượng user của class User với các thuộc tính là :name, :age

I. Bảng so sánh tổng quát

Method Uses Default Accessor Saved to Database Validations Callbacks Update updated_at column Readonly check
attribute= assign_attribute yes no n/a n/a n/a n/a
write_attribute no no n/a n/a n/a n/a
update_attribute yes yes no yes yes yes
attributes= yes no n/a n/a n/a n/a
update update_attributes yes yes yes yes yes yes
update_column no yes no no no yes
update_columns no yes no no no yes
User::update yes yes yes yes yes yes
User::update_all no yes no no no no
user.increment(:age, 10) user.decrement(:age, 10) no n/a n/a n/a n/a
user.increment!(:age, 10) user.decrement!(:age, 10) yes no yes yes yes
  • Touches updated_at: Các phương thức khi thực hiện có cập nhật giá trị mới cho trường created_at hay không.
  • Readonly check: Với những phương thức có check readonly, chúng sẽ raise exception nếu có bất kì cột nào được đánh dấu là readonly

II. Chi tiết các phương thức

1. user.name = "Rob"

Đây có lẽ là phương thức phổ biến và dễ sử dụng nhất. Với phương thức này, thuộc tính name sẽ được thay đổi và chưa được lưu vào database cho tới khi lệnh save được gọi. Với các thay đổi chưa được lưu vào database bạn có thể kiểm tra bằng câu lệnh user.changed?. Bạn cũng có thể reload các thay đổi tạm thời này bằng lệnh user.reload

2. user.write_attribute(:name, "Rob")

Phương thức này được gọi bởi accessor mặc định phía trên. Ngoài ra nó còn có cách viết tương đương là user[:name] = "Rob". Các thay đổi của phương thức này không được lưu vào database. Ví dụ:

def name=(new_name)
  write_attribute(:name, new_name.upcase)
  # This is equivalent:
  # self[:name] = new_name.upcase
end

3. user.update(name: "Rob")user.update_attributes(name: "Rob")

Trên Rails 4 ta có phương thức user.update và phiên bản trước đó của nó trên Rails 3 là user.update_attributes. Đây là phương thức rất phổ biến, được dùng để thay đổi một hoặc nhiều thuộc tính của đối tượng và lưu các thay đổi này vào database. Ví dụ khi update nhiều thuộc tính

user.update(name: "sadfdasfa", age: 22)

4. user.update_attribute(:name, "Rob")

Phương thức này được dùng để thay đổi một thuộc tính của đổi tượng và lưu các thay đổi này vào database. Khi sử dụng phương thức này, các validate sẽ không được gọi, tương tự như khi bạn sử dụng hàm save(validate: false)

Do các validate không được gọi nên dẫn tới việc dữ liệu sai có thể được lưu vào database. Do đó user.updateuser.update_attributes thường được sử dụng thay vì user.update_attribute

5. user.attribute=user.assign_attribute

Hai phương thức này gán giá trị cho một hoặc nhiều thuộc tính của đối tượng tuy nhiên chưa lưa các thay đổi này vào database. Ví dụ:

user.attributes = {name: "Rob", age: 12}
user.assign_attributes {name: "Rob", age: 12}

Hai cách dùng này là tương đương nhau.

6. user.update_columns(name: "Rob")

Phương thức này sẽ gọi trực tiếp câu truy vấn SQL UPDATE để thay đổi một hoặc nhiều thuộc tính. Do câu truy vấn SQL được gọi trực tiếp mà không gọi qua hàm save nên các validates và callbacks sẽ bị bỏ qua. Ngoài ra nó cũng kiểm tra nếu có bất kì columns nào được đánh dấu readonly thì sẽ raise ra lỗi. Do tất cả callbacks và validates sẽ không được gọi nên phương thức này ít được dùng trong chương trình hơn so với update_attributes nó sẽ hữu ích khi bạn muốn khởi tạo nhanh các giá trị để test các chức năng mà không liên quan đến validates và callbacks.

7. user.update_column(:name, "Rob")

Tương tự như user.update_columns(name: "Rob") nhưng phương thức này chỉ dùng để thay đổi một thuộc tính

8. User.update(1, name: "Rob")

Lại một phương thức update khác tuy nhiên đây là một class method không phải instance method như hàm update được giới thiệu ở mục 3. Với phương thức này bạn có thể dùng để thay đổi nhiều thuộc tính của nhiều đối tượng cùng lúc. Khi muốn thay đổi nhiều đối tượng cùng lúc, ta có hai cách viết khác nhau

Cách 1:

User.update(
  [1,2,3],
  [
    {name: "Rob"},
    {name: "David", age: 12},
    {age: 15, location: "London"},
  ]
)

Cách 2:

User.update(1 => {name: "Rob"}, 2 => {name: "David", age: 12}, 3 => {age: 15, location: "London"})

9. User.update_all(name: "Rob")

Đây cũng là một class method khác và phương thức này cũng chạy trực tiếp câu truy vấn SQL UPDATE nên nó cũng lưu các thay đổi và database nhưng bỏ qua validates và callbacks.

Ví dụ đổi tên tất cả các user tên "Robbie" thành "Rob"

User.where(name: "Robbie").update_all(name: "Rob")

10. user.increment(:age, 10)user.decrement(:age, 10)

Đây là hai hàm được dùng để tăng hoặc giảm giá trị của một thuộc tính nào đó đi một số. Những giá trị thay đổi này sẽ không được lưu vào database. Tuy nhiên sau khi dùng hàm này, bạn kiểm tra bằng lệnh user.changed? sẽ trả về kết quả false. Nếu giá trị ban đầu là nil thì kết quả sẽ là 0.

11. user.increment!(:age, 10)user.decrement!(:age, 10)

Khác với hai hàm trên, hai hàm này sẽ lưu các thay đổi vào database sử dụng hàm update_attribute. Do đó nó cũng gọi các callback nhưng không check validate tương tự như khi bạn sử dụng update_attribute