ActiveRecord serialize trong Rails
Bài đăng này đã không được cập nhật trong 8 năm
Khi lập trình rails,đã bao giờ bạn muốn lưu trữ và truy xuất 1 object vào cơ sở dữ liệu mà không biết phải làm cách nào. ActiveRecord#Serialize
chính là giải pháp đơn giản nhất để giải quyết vấn đề đó. Và hôm nay tôi muốn hướng dẫn cho bạn những điều cơ bản nhất về kỹ thuật này.
1 Khai báo
Bạn có trước hết bạn có 1 model như sau.
class User < ActiveRecord::Base
serialize :properties
end
Rails cung cấp một nền tảng cơ bản trong việc lưu trữ dữ liệu là 1 object trong ActiveRecord
với phương thức serialize
. Chúng ta cần chuẩn bị một trường text
trong cơ sở dữ liệu để lưu trữ cũng như truy xuất object mà ta khai báo trong model User, trên đây là trường properties
$ rails generate migration AddPropertiesToUser properties:text
$ rake db:migrate
Nào giờ bạn vào console
và kiểm tra lại xem chúng ta đã có trường properties chưa
irb(main):001:0> user = User.create
irb(main):002:0> user.properties #=> nil
2 Lưu Hash vào cơ sở dữ liệu
Giờ khi đã khai bảo với hướng dẫn ở trên bạn hoàn toàn có thể lưu 1 object vào trường properties
. Và chúng ta sẽ thử với Hash
object :
irb(main):003:0> user.properties = {twitter_handle: "@BrianVanLoo", location: "CA"}
irb(main):004:0> user.save
Giờ bạn đã lưu thành công và hãy kiểm tra thử xem cái gì đã được lưu trữ ở trường properties
trong cơ sở dữ liệu.
---\n:twitter_handle: '@BrianVanLoo'\n:location: CA\n
Vậy đây có phải là string
? Nó là YAML đại diện cho hash trước đó ta lưu vào. YAML là ngôn ngữ mặc định cho serialized atrribute
nó cho phép ta lưu trữ 1 object và lấy nó ra với chính xác kiểu của nó.
irb(main):005:0> user = User.last
irb(main):006:0> user.properties #=> {:twitter_handle=>"@BrianVanLoo", :location=>"CA"}
Ngoài YAML bạn cũng có thể sử dụng JSON
để mã hóa bằng cách khai báo như sau:
class User < ActiveRecord::Base
serialize :properties, JSON
end
Giờ hãy xem object của chúng ta được lưu trong cơ sở dữ liệu như thế nào:
{"twitter_handle":"@BrianVanLoo","location":"CA"}
Bạn cũng hết sức lưu rằng khi dữ liệu được đưa vào cơ sở dữ liệu với các key là các symbol
khi lấy ra với cách mã hóa bằng định dang JSON
các key này sẽ chuyển hết thành string
irb(main):007:0> user = User.last
irb(main):008:0> user.properties #=> {"twitter_handle"=>"@BrianVanLoo", "location"=>"CA"}
3 Lưu các object phức tạp
Ở trên trên bạn đã có thể lưu 1 object đơn giản như Hash
vào cơ sở dữ liệu. Vậy với những object có cấu truc phức tập hơn thì sao? Ta có một ví dụ như sau :
<%= form_for @user.properties do |f| %>
#...
<% end %>
Vấn đề đầu tiên gặp phải trong ví dụ này đó là properties
của @user
(được khai báo trong controller @user = User.new
) sẽ nhận thông báo lỗi do hiện tại nó nhận giá trị là nil
ActionView::Template::Error (undefined method `model_name' for NilClass:Class):
1: <%= form_for @user.properties do |f| %>
2: #...
3: <% end %>
Bạn có thể giải quyết bằng cách khai báo properties
như là 1 hash rông trong controller:
@user.properties ||= {}
Giờ bạn lại tiếp tục nhận được một thông báo lỗi khác:
ActionView::Template::Error (undefined method `model_name' for Hash:Class):
1: <%= form_for @user.properties do |f| %>
2: #...
3: <% end %>
Trường hợp này form_for
muốn object phải dưới dạng ActiveRecord
. Chúng ta phải sửa lại model Properties
như sau:
class User
class Properties
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :twitter_handle, :location
def persisted?; true end
def id; 1 end
end
end
Quay trở lại với User
model chúng ta đổi lại định dạng lưu trữ JSON
thành object Properties
chúng ta mới tạo:
class User < ActiveRecord::Base
serialize :properties, Properties
end
KHi chúng ta tạo mới 1 User
tức là đồng thời chúng ta cũng tạo mới 1 Properties
:
irb(main):007:0> user = User.new
irb(main):008:0> user.properties #=> #<User::Properties:0x9c579e4>
Giờ bạn đã có thể xóa dòng khai báo
@user.properties ||= {}
mà ta viết trong controller trước đó.
Object Properties
giờ đây sẽ được mã hóa bằng YAML vào sẽ có dạng trong cơ sở dữ liệu như sau:
--- !ruby/object:User::Properties {}\n
Chúc các bạn thành công
All rights reserved