JSON Serialized Columns with Rails
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 ...
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