Reputation: 1
I'm using ActiveModel::Serializer in a rails application to format my model data as a json response, but I would like to change the formatting so that the associations of my main model are not nested. I tried setting root: false and that doesn't work
I have a model Account
with an association belongs_to :account_status
and I was able to add this association in the AccountSerializer
to get that associated data just fine. But do to my api contract requirements, I need the json to be formatted without the association nesting.
So I'm getting this:
{
"account_id": 1
<other account info>
...
"account_status": {
"status_code": 1
"desc": "status description"
....
}
}
But I want this:
{
"account_id": 1
<other account info>
...
"account_status_status_code": 1
"account_status_desc": "status description"
....
}
How can I achieve the expected behavior without writing each account_status
field as an individual attribute in the AccountSerializer
??
Controller
class AccountsController < ActionController::API
def show
account = Account.find(params[:account_id])
render json: account
end
end
Model
class Account < ActiveRecord::Base
self.primary_key = :account_id
belongs_to :account_status, foreign_key: :account_status_code, inverse_of: :accounts
validates :account_status_code, presence: true
end
Serializer
class AccountSerializer < ActiveModel::Serializer
attributes(*Account.attribute_names.map(&:to_sym))
belongs_to :account_status,
foreign_key: :account_status_code,
inverse_of: :accounts
end
OS Type & Version: macOS Catalina v 10.15.7 Rails 6.1.4:
ActiveModelSerializers Version 0.10.0:
Output of ruby -e "puts RUBY_DESCRIPTION"
:
ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-darwin19]
Upvotes: 0
Views: 172
Reputation: 102222
Instead of using an assocation in your serializer you can setup delegation in your model:
class Account < ApplicationRecord
delegate :desc, ...,
to: :account_status,
prefix: true
end
This will create a account_status_desc
method which you can simply call from your serializer:
class AccountSerializer < ActiveModel::Serializer
attributes(
:foo,
:bar,
:baz,
:account_status_code, # this is already a attribute of Account
:account_status_desc
# ...
)
end
Another way of doing this is by simply adding methods to your serializer:
class AccountSerializer < ActiveModel::Serializer
attributes :foo
def foo
object.bar.do_something
end
end
This is a good alternative when the result of serialization does not align with the internal representation in the model.
Upvotes: 0
Reputation: 3811
you could replace belongs_to :account_status,...
by below code
class AccountSerializer < ActiveModel::Serializer
attributes(*Account.attribute_names.map(&:to_sym))
AccountStatus.attribute_names.each do |attr_name|
key = "account_status_#{attr_name}"
define_method(key) do
# i checked and see that `object.account_status` just call one time
object.account_status.send(attr_name)
end
attribute key.to_sym
end
end
Upvotes: 0