Ruby on rails prevent serializer nest json - ruby-on-rails

it's a simple question
I have a controller
class Api::ColorsController < ApplicationController
def index
colors = ProductCustom.where(product_id: params[:product_id], wheel_id: params[:wheel_id])
render json: colors, each_serializer: ProductCustomsSerializer, adapter: :json, root: "data"
end
end
ant two serializers
class ProductCustomsSerializer < ActiveModel::Serializer
belongs_to :color, serializer: ColorSerializer
end
class ColorSerializer < ActiveModel::Serializer
attributes :id, :color
end
The response is fine but I'm getting nested objects. how can I prevent that?
{
"data":[
{
"color":{
"id":11,
"color":"green"
}
},
{
"color":{
"id":12,
"color":"blue"
}
}
]
}
what I want
{
"data":[
{
"id":11,
"color":"green"
},
{
"id":12,
"color":"blue"
}
]
}

As your controller name suggests and the output you are looking for it seems to indicate the outcome is to serialize Color objects not ProductCustom objects.
If that is the case based upon the params your controller is receiving and assuming there is a belongs_to relation from your ProductCustom to Color on your ActiveModel you may want to try:
class Api::ColorsController < ApplicationController
def index
# Query for the desired ProductCustoms objects and include the associated
# Color objects. From the result set, map the Color objects into the array
colors = ProductCustom
.includes(:color)
.where(product_id: params[:product_id], wheel_id: params[:wheel_id])
.map(&:color)
# You many want to add a trailing .uniq to the above if several ProductCustom
# use the same Color and you don't want duplicates.
# Note, Rails should be able to determine the serializer to use but you
# can always specify `each_serializer: ColorSerializer` if you really want
render json: colors, adapter: :json, root: "data"
end
end

Related

Adding global root key and specific root key for each serialized item with active model serializer

I am using active-model-serializer. I have a collection of objects that I need to return as a JSON in a special formal. Here's what I have written so far:
#tickets = Ticket.where(status: "PLACED")
render json: #tickets, root: 'placed', each_serializer: ItemSerializer
Here's my item serializer:
class ItemSerializer < ApplicationSerializer
attributes :pool_id, :selections
def root
"params"
end
end
Here's the response with the current code:
[{\"pool_id\":759,\"selections\":\"1/2/3/4/5/6/7/8\"}]
I want to be able to add a root key "params" to each element of the array and a global root key "placed" before the array, so the desired output would be:
{ "placed": [
{
"params": {
"pool_id": 123,
"selections": "1/1/1"
}
}
]
}
How can I achieve that with an active model serializer?
For the global root key, I needed to add adapter: :json to the render call
render json: #tickets, root: 'placed', each_serializer: BatchItemSerializer, adapter: :json
To add a key at the root of each serialized element, you can overwrite the attributes method in the serializer. In this specific case, you can do it like this:
def attributes(*args)
hash = super
{ params: hash }
end

Rails - activerecord model serializer group results by specific column in the foreign table

I want to use the ActiveRecord model serializer to show results from the primary key table and foreign key table. However, I want the results to be presented grouped by a column in the foreign key table.
cats = Cats.paginate(page: params[:page], per_page: 20)
render json: cats, meta: pagination(cats), adapter: :json
In the ActiveRecord model serializer:
class CatSerializer < ActiveModel::Serializer
attributes :cat, :persons
def persons
object.person
end
def cat
{ id: object.id, cat_name: object.name}
end
Now cat_name is not unique and Persons can share many cat_names. please note that Person => has_many Cats, but cat_name can be similar to multiple Persons. How can I show the data in this format:
"results": [
{
"cat": {
"id": 11,
"cat_name": "Luzi",
...
},
"persons": [
{
"id": 1,
"name": "andy"
},
{
"id": 2,
"name": "david"
}
Please also note that groyp_by(&:cat_name) does not work with pagination.
You can use custom serializer that accepts an already groupby ActiveRecord result
def index
#cats = Cat.joins(:persons).group("persons.name")
render json: #cats, serializer: GroupedCatSerializer
end
And you can define custom serializer like
class GroupedCatSerializer < ActiveModel::Serializer
# method override
def serializable_object(options={})
#object.map do |group_key, models|
[ group_key , serialized_models(models) ]
end.to_h
end
private
def serialized_models models
models.map{ |model| CatSerializer.new(model, root:
false) }
end
end

Rendering json data fields from mysql db in rails response object

I have a rails 4.2 app that uses mysql db 5.7 which supports json fields. So my user model has a field called display_pic which is a json object.
class User < ActiveRecord::Base
serialize :display_pic, JSON
....
In the action get_user I render user as follows
def get_user
#u = User.where(...)
render json: { user: #u }
end
The problem is that the json field display_pic doesn't come out as a nested json object, rather it is rendered as a string. I would like to have a response like the following
{
"user": {
"name": "some name",
"email": "some email",
"display_pic": {
"url": "http://someurl.com",
"width": "400px",
}
}
}
Probably a better way to do this, but you can format it as json in the serializer.
class UserSerializer < ActiveModel::Serializer
attribute :name
attribute :email
attribute :display_pic
def display_pic
JSON.parse(object.display_pic)
end
end
Use the following code it will solve your problem:
def get_user
#u = User.where(...)
render json: { user: JSON.parse(#u)}
end
Have you tried to use the method .as_json ?
def get_user
#u = User.where(...)
render json: #u.as_json
end
You should have not need to set serialize :display_pic, :JSON, but you can overload the method in your user.rb class in order to get on response references or methods results authomatically loaded on your front end:
class PlayerCharacter < ApplicationRecord
[...]
def as_json(options = {})
super(options.merge(include: [ :reference1, :reference2]).merge(methods: [:method_name1, :method_name2])
end
end
EDIT:
you could add display_pic as follow:
class PlayerCharacter < ApplicationRecord
[...]
def as_json(options = {})
super(options.merge(include: [ :display_pic])
end
end

In Rails, how can you return model data from multiple different ActiveModelSerializers/Models at once?

I have a couple models. Let's call them Widget and Gadget.
My #index for for Widget and Gadget looks something like this
def index
widgets = Widget.all
if widgets
respond_with widgets, each_serializer: Api::V1::WidgetSerializer
else
render json: { message: [t(:not_found_widget)] }, status: :not_found
end
end
def index
gadgets = Gadget.all
if gadgets
respond_with gadgets, each_serializer: Api::V1::GadgetSerializer
else
render json: { message: [t(:not_found_gadget)] }, status: :not_found
end
end
And my serializers...
class Api::V1::WidgetSerializer < ActiveModel::Serializer
attributes :id, :desc
end
class Api::V1::GadgetSerializer < ActiveModel::Serializer
attributes :id, :desc
end
However, I have the need for a resource that returns both of those in 1. I need both widgets and gadgets returned at once. So the json would look like...
{
"widgets": [
{
"id": 1,
"desc": "One"
},
{
"id": 2,
"desc": "Two"
}
],
"gadgets": [
{
"id": 1,
"desc": "One"
},
{
"id": 2,
"desc": "Two"
}
]
}
How can I achieve this. Something like
widgets = Widget.all
gadgets = Gadget.all
respond_with widgets, each_serializer: Api::V1::WidgetSerializer, gadgets, each_serializer: Api::V1::GadgetSerializer
However, this clearly doesn't work.
Serializer classes don't have to match AR models. Serializer classes should be used as your representation of your JSON you want to produce. In your example, let's assume to call a new serializer DashboardSerializer, then you can put both widgets and gadgets there:
class DashboardSerializer < ActiveModel::Serializer
self.root = false
attributes :widgets, :gadgets
def widgets
Widget.all
end
def gadgets
Gadget.all
end
end
The only way I can see this working is if you had a model that has_many widgets and has_many gadgets with it's own serializer as AMS will apply the correct serializers to associations declared in a serializer.
something like
class Composite < ActiveRecord::Base
has_many :widgets
has_many :gadgets
end
class CompositeSerializer < ActiveModel::Serializer
attributes :id
has_many :widgets
has_many :gadgets
end

Bad formatted json

There is a model Event in my app and I have two problems, the first is the returned json that comes messed with the request:
$ curl http://0.0.0.0:3000/events
it gives me
{
"events":[
{"events":{"name":"First"}},
{"events":{"name":"Second"}}],
"meta":{"total_pages":9,
"next_page":"http://0.0.0.0:3000/events?page=2"}
}
which I think it should gives
{
"events":[
{"name":"First" },
{"name":"Second"}
],
"meta":{
"total_pages":9,
"next_page":"http://0.0.0.0:3000/events?page=2"
}
}
also I am using the gems draper and active_model_serializer, and the second problem is that the json format doesn't respect the Serializer definition:
class EventSerializer < ActiveModel::Serializer
attributes :id, :date_end
end
I would like to know where should I look to fix these problems.
Answering the questions, the code that generate the json:
class EventsController < ApplicationController
def index
#events = Event.order(:name).page(params[:page])
#events = #events.where(id: params[:ids].split(',')) if params[:ids].present?
render json: #events.decorate, meta: { total_pages: #events.total_pages, next_page: next_page_url(#events)}
end
def next_page_url(event)
unless event.last_page?
next_page = event.current_page + 1 unless event.last_page?
events_url(params.merge({:page => next_page}))
end
end
and the decorator:
class EventDecorator < Draper::Decorator
delegate :current_page, :total_pages, :limit_value
def name
object.name
end
end

Resources