JSON Serialized Columns with Rails
Bài đăng này đã không được cập nhật trong 6 năm
1. Lời tựa
Khi làm việc với Rails , hay cụ thể ở đây mình muốn nói làm việc với model trong rails. Bình thường các bạn thường lưu một giá trị trên một column. Nhưng bài toán ở đây là giá sử bạn muốn lưu một mảng giá trị cho một column hay thậm trí một hash. Vậy sẽ giải quyết thế nào? Vâng giải pháp ở đây chính là "Serialized Columns"
2. Tạo model với Serialized Columns
# Migration for adding a JSON column to the users table.
def change
add_column :users, :facebook_profile_data, :json
end
# user.rb
class User < ApplicationRecord
#...
serialize :facebook_profile_data, JSON
end
Và bậy giờ đơn giản có thé chạy:
user = User.create!({
username: 'usama.ashraf',
facebook_profile_data: {
object_id: '...',
profile_image_url: '...'
}
})
user.facebook_profile_data
# {'object_id' => '...', 'profile_image_url' => '...'}
user.facebook_profile_data['profile_image_url']
user.facebook_profile_data['object_id'] = 'new-object-id'
user.save
Việc tạo như vậy nhiều khi thực sự là không cần thiết. Nhưng ở đây mình thực sự không muốn tạo thêm một bảng nên sẽ làm như vậy. 3. Custom serializer
- Chúng ta sẽ sửa lại model trên một chút:
# models/user.rb
class User < ApplicationRecord
#...
serialize :facebook_profile_data, FacebookProfileData
end
# models/facebook_profile_data.rb
class FacebookProfileData
attr_accessor :object_id, :profile_image_url
include Serializable
end
Và sau đó thêm custom như sau:
# models/concerns/serializable.rb
module Serializable
extend ActiveSupport::Concern
def initialize(json)
unless json.blank?
if json.is_a?(String)
json = JSON.parse(json)
end
# Will only set the properties that have an accessor,
# such as those provided to an attr_accessor call.
json.to_hash.each { |k, v| self.public_send("#{k}=", v) }
end
end
class_methods do
def load(json)
return nil if json.blank?
self.new(json)
end
def dump(obj)
# Make sure the type is right.
if obj.is_a?(self)
obj.to_json
else
raise StandardException, "Expected #{self}, got #{obj.class}"
end
end
end
end
Sau khi custom chúng tạo tạo như thế này sẽ lỗi.
# This will now raise an error because of the type mismatch.
user = User.create!({
username: 'usama.ashraf',
facebook_profile_data: {
object_id: '...',
profile_image_url: '...'
}
})
Mà sẽ phải là:
user = User.create!({
username: 'usama.ashraf',
facebook_profile_data: FacebookProfileData.new({
object_id: '...',
profile_image_url: '...'
})
})
Nếu ta có để ý thì dòng này:
json.to_hash.each { |k, v| self.public_send("#{k}=", v) }
Dòng đảm bảo các attr phải được attr_accessor
Và bây giờ bạn có thẻ dễ dàng thêm một attr mới:
# models/facebook_profile_data.rb
class FacebookProfileData
attr_accessor :object_id, :profile_image_url, :city, :birthday
include Serializable
end
3. Note
Trên đó là cách mình đã tạo 1 serialize json ngoài ra bạn có thể tạo vơi aray...
4.Tham khảo
https://codeburst.io/json-serialized-columns-with-rails-a610a410fcdf
All rights reserved