【问题标题】:ActiveModel Serializers: has_many with condition at run-time?ActiveModel 序列化器:has_many 在运行时有条件?
【发布时间】:2017-02-15 08:33:28
【问题描述】:

我使用 rails (5.0.1) 和 active_model_serializers (0.10.2)。我想以某种方式有条件地序列化has_many 关联:

class Question < ApplicationRecord
    has_many :responses, :inverse_of => :question
end

class Response < ApplicationRecord
    belongs_to :question, :inverse_of => :responses
end

class QuestionSerializer < ActiveModel::Serializer
  attributes :id, :title, :created_at, :updated_at
  has_many :responses
end

class ResponseSerializer < ActiveModel::Serializer
  attributes :id, :title
end

我使用 jsonapi 并查询 http://localhost:3000/api/questions/1 我得到这个回复:

Response-1

{
  "data": {
    "id": "1",
    "type": "questions",
    "attributes": {
      "title": "First",
      "created-at": "2017-02-14T09:49:20.148Z",
      "updated-at": "2017-02-14T13:55:37.365Z"
    },
    "relationships": {
      "responses": {
        "data": [
          {
            "id": "1",
            "type": "responses"
          }
        ]
      }
    }
  }
}

如果我从QuestionSerializer 中删除has_many :responses,我会得到:

响应 2

{
  "data": {
    "id": "1",
    "type": "questions",
    "attributes": {
      "title": "First",
      "created-at": "2017-02-14T09:49:20.148Z",
      "updated-at": "2017-02-14T13:55:37.365Z"
    }
  }
}

我如何有条件地在运行时获得 Response-1Response-2?我尝试了所有找到的建议 - 都不适用于 AMS 0.10.2。目前,该条件仅以这种方式起作用:

class QuestionSerializer < ActiveModel::Serializer
  attributes :id, :title, :created_at, :updated_at
  has_many :responses if true
end

或者:

class QuestionSerializer < ActiveModel::Serializer
  attributes :id, :title, :created_at, :updated_at
  has_many :responses if false
end

在这两种情况下,我确实得到了 Response-1Response-2。但这是硬编码的,我想可能将参数传递给序列化程序或做一些类似的事情。

我该怎么办?

【问题讨论】:

    标签: ruby-on-rails active-model-serializers


    【解决方案1】:

    我认为您已经回答了自己的问题。如果您查看 AMS documentation for associations,它会说支持条件。

    据我所知,你只是一个错字

    class QuestionSerializer < ActiveModel::Serializer
       has_many :responses, if: false
    end
    

    attributes 方法还支持if 选项,如here 所述。

    你的active_model_serializers 是什么版本?

    编辑: 我的回答也有错误。我正在使用active_model_serializers (0.10.3),我可以做到

    class QuestionSerializer < ActiveModel::Serializer
       has_many :responses, if: -> { false }
    end
    

    if 选项适用于方法、过程或字符串。我认为您可以在运行时通过提供一个方法作为条件来决定。

    class QuestionSerializer < ActiveModel::Serializer
      attr_writer :should_render_association
      has_many :responses, if: -> { should_render_association }
    end
    # Usage: 
    serializer = QuestionSerializer.new(question)
    serializer.should_render_association = false
    serializer.to_json
    # => no "responses" key
    

    【讨论】:

    • 我在文档中看到了。我使用 AMS 0.10.2。感到惊讶 - if: 条件不起作用(!),只有 if 起作用。即使有文档!服务器说:syntax error, unexpected ':'。但我的问题与它无关。
    • 我更新了答案,我认为它有效。抱歉,如果我遗漏了什么,我认为重点是有条件地渲染一个属性,但在运行时决定。
    【解决方案2】:

    感谢@gkats,我找到了答案(AMS 0.10.2):

    class QuestionSerializer < ActiveModel::Serializer
      attributes :id, :title, :created_at, :updated_at
      has_many :responses, if: -> { should_render_association }
    
      def should_render_association
        @instance_options[:show_children]
      end
    end
    
    class Api::ResponsesController < Api::ApplicationController
      def show
        render json: @response, show_children: param[:include_children]
      end
    end
    

    问题在于语法:序列化程序中的if: 应该应用于块而不是函数。

    【讨论】:

      【解决方案3】:

      我在同样的问题上苦苦挣扎。这是我想出的可行解决方案。

      使用except: [:key_one, :key_two] 作为参数进行初始化。

      class QuestionsController
          def index
              @questions = Question.all
              render(json: ActiveModel::ArraySerializer.new(@questions,
                                                            each_serializer: QuestionSerializer,
                                                            root: 'questions',
                                                            except: [:responses])
              )
          end
      
          def show 
              # you can also pass the :except arguments here
              # render(json: QuestionSerializer.new(@question, except: [:responses]).to_json)
              render(json: QuestionSerializer.new(@question).to_json)
          end
      end
      

      https://www.rubydoc.info/gems/active_model_serializers/0.9.3/ActiveModel%2FArraySerializer:initialize

      https://www.rubydoc.info/gems/active_model_serializers/0.9.1/ActiveModel%2FSerializer:initialize

      【讨论】:

        【解决方案4】:

        您可以通过以下方式从父序列化程序传递参数,并在子序列化程序中根据这些参数显示或隐藏属性。

        父序列化器:

        class LocationSharesSerializer < ActiveModel::Serializer
          attributes :id, :locations, :show_title, :show_address
             
          def locations
            ActiveModelSerializers::SerializableResource.new(object.locations, {
              each_serializer: PublicLocationSerializer,
              params: { 
                show_title: object.show_title
              },
            })
          end
        
        end
        

        子序列化程序

        class PublicLocationSerializer < ActiveModel::Serializer
          attributes :id, :latitude, :longitude, :title, :directions, :description, :address, :tags, :created_at, :updated_at, :photos
        
          def title
            object.title if @instance_options[:params][:show_title]
          end
        
        end
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-11-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多