Reputation: 2376
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?
Upvotes: 7
Views: 9601
Reputation: 4182
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
Upvotes: 0
Reputation: 356
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.1/ActiveModel%2FSerializer:initialize
Upvotes: 0
Reputation: 2376
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.
Upvotes: 5
Reputation: 1322
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
Upvotes: 6