开发者

defaults for to_json in Rails with :include

Let us say I have a model Post which belongs to a User. To convert to json, I do something like this

@reply.to_json(:include => {:user => {:only => [:email, :id]}, 
               :only => [:title, :id])

However, I want to set开发者_JAVA技巧 some defaults for this so I don't have to specify :only everytime. I am trying to override as_json to accomplish this. When I add as_json in User model, it is called when I do @user.to_json but when user is included in @reply.to_json, my overriden as_json for User is ignored.

How do I make this work?

Thanks


You can do this by overriding serializable_hash in your model, like so:

class Reply < ActiveRecord::Base
  def serializable_hash(options={})
    options = { 
      :include => {:user => {:only => [:email, :id]}, 
      :only => [:title, :id]
    }.update(options)
    super(options)
  end
end

This will affect all serializable methods, including serializable_hash, to_json, and to_xml.


In the Reply model:

def as_json(options = {})
   super options.merge(:methods => [:user], :only => [:id, :title])
end

And in the User model:

def as_json(options = {})
   super options.merge(:only => [:id, :email])
end

When the association is included as a method and not an include, it will run the .to_json on it.


You could add another method to ActiveRecord::Base, like self.to_minimal_json(*only_fields) and build your .to_json call there.

unspectacular, but may work.


So I found out that the problem is that for includes, instead of calling as_json to generate the json, Rails directly calls serializable_hash. Monkey patching fixes the problem. Put this in an initializer

module ActiveRecord #:nodoc:
  module Serialization

    def serializable_hash(options = nil)
      options = options.try(:clone) || {}

      options[:except] = Array.wrap(options[:except]).map { |n| n.to_s }
      options[:except] |= Array.wrap(self.class.inheritance_column)

      hash = super(options)

      #serializable_add_includes(options) do |association, records, opts|
        #hash[association] = records.is_a?(Enumerable) ?
        #records.map { |r| r.serializable_hash(opts) } :
        #records.serializable_hash(opts)
      #end

      serializable_add_includes(options) do |association, records, opts|
        hash[association] = records.is_a?(Enumerable) ?
          records.map { |r| r.as_json(opts.merge(:no_root => true)) } :
          records.as_json(opts.merge(:no_root => true))
      end

      hash
    end
  end
end

module ActiveModel
  # == Active Model JSON Serializer
  module Serializers
    module JSON

      def as_json(options = nil)
        hash = serializable_hash(options)

        if include_root_in_json and !options[:no_root]
          custom_root = options && options[:root]
          hash = { custom_root || self.class.model_name.element => hash }
        end

        hash
      end

    end
  end
end

Notice how the serializable_hash call is replaced with as_json (with an extra parameter to supress addition of root in includes). Now if you has as_json in your model, it will be called even in case of includes


As options might under circumstances be nil, we'd better be aware of it:

module ActiveModel
  # == Active Model JSON Serializer
  module Serializers
    module JSON

      def as_json(options)
        options ||= {}
        hash = serializable_hash(options)

        if include_root_in_json and !options[:no_root]
          custom_root = options && options[:root]
          hash = { custom_root || self.class.model_name.element => hash }
        end

        hash
      end

    end
  end
end
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜