ActiveModel Serializers: has_many with condition at run-time?

こ雲淡風輕ζ 提交于 2019-12-13 13:04:17

问题


I use rails (5.0.1) and active_model_serializers (0.10.2). I would like to somehow conditionally serialize the has_many associations:

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

I use jsonapi and querying http://localhost:3000/api/questions/1 I get this response:

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"
          }
        ]
      }
    }
  }
}

If I remove has_many :responses from QuestionSerializer I get this:

Response-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"
    }
  }
}

How do I conditionally get either Response-1 or Response-2 at run time? I tried all the recommendations found - neither works with AMS 0.10.2. Currently, the condition works only this way:

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

OR:

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

In these 2 cases I really get either Response-1 or Response-2. But this is hard-coded and I would like to maybe pass a param into the serializer or do some similar thing.

What should I do?


回答1:


I think you've kind of answered your own question. If you look into the AMS documentation for associations it says that conditionals are supported.

From what I can tell you're just a typo away

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

The attributes method also supports the if option, as described here.

What's your active_model_serializers version?

EDIT: I have an error in my answer too. I'm using active_model_serializers (0.10.3) and I'm able to do

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

The if option works with either methods, procs or strings. I think you can decide at runtime by providing a method as the conditional.

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



回答2:


Thanks to @gkats, I found an answer (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

The problem was all about the syntax: if: in the serializer should be applied to a block rather than to a function.




回答3:


I struggled a lot with the same issue. Here is working solution I figured.

Initialise with except: [:key_one, :key_two] as arguments.

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



来源:https://stackoverflow.com/questions/42244237/activemodel-serializers-has-many-with-condition-at-run-time

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!