简体   繁体   English

ActiveModel Serializers:has_many 在运行时有条件?

[英]ActiveModel Serializers: has_many with condition at run-time?

I use rails (5.0.1) and active_model_serializers (0.10.2).我使用 rails (5.0.1) 和 active_model_serializers (0.10.2)。 I would like to somehow conditionally serialize the has_many associations:我想以某种方式有条件地序列化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

I use jsonapi and querying http://localhost:3000/api/questions/1 I get this response:我使用 jsonapi 并查询http://localhost:3000/api/questions/1我得到这个响应:

Response-1 :响应 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:如果我从QuestionSerializer删除has_many :responses我得到这个:

Response-2 :响应 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?如何在运行时有条件地获得Response-1Response-2 I tried all the recommendations found - neither works with AMS 0.10.2.我尝试了找到的所有建议 - 都不适用于 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 .在这两种情况下,我真的得到了Response-1Response-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?我该怎么办?

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

The problem was all about the syntax: if: in the serializer should be applied to a block rather than to a function.问题全在于语法: if:在序列化器中应该应用于块而不是函数。

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.如果您查看关联的 AMS 文档,它会说支持条件。

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 .attributes方法也支持if选项,描述在这里

What's your active_model_serializers version?你的active_model_serializers版本是什么?

EDIT : I have an error in my answer too.编辑:我的答案也有错误。 I'm using active_model_serializers (0.10.3) and I'm able to do我正在使用active_model_serializers (0.10.3)并且我能够做到

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

The if option works with either methods, procs or strings. if选项适用于方法、过程或字符串。 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

I struggled a lot with the same issue.我在同样的问题上挣扎了很多。 Here is working solution I figured.这是我想出的工作解决方案。

Initialise with except: [:key_one, :key_two] as arguments.使用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.3/ActiveModel%2FArraySerializer:initialize

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

Here is how you can pass parameters from the parent serializer and show or hide attributes based on these parameters in the child serializer.以下是如何从父序列化程序传递参数并根据这些参数在子序列化程序中显示或隐藏属性的方法。

Parent serializer:父序列化器:

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

Child serializer子序列化器

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM