21/11/2018, 23:46

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

0