defaults for to_json in Rails with :include

后端 未结 5 965
悲&欢浪女
悲&欢浪女 2020-12-29 23:24

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 =>         


        
相关标签:
5条回答
  • 2020-12-30 00:06

    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.

    0 讨论(0)
  • 2020-12-30 00:09

    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

    0 讨论(0)
  • 2020-12-30 00:18

    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.

    0 讨论(0)
  • 2020-12-30 00:20

    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.

    0 讨论(0)
  • 2020-12-30 00:28

    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 讨论(0)
提交回复
热议问题