Reputation: 2107
I have an Account
model that has columns:
id :bigint(8)
name :string
In app/models/account.rb
, I have:
class Account < ApplicationRecord
has_one_attached :logo
end
This is an API, and the controller only returns JSON. In my controller, I have:
render :json => Account.all
The code above returns:
[
{
"id": 1,
"name": "Example1"
},
{
"id": 2,
"name": "Example2"
}
]
I want to also include the url of the ActiveStorage
file attached to the JSON returned.
In order to achieve that, I am doing this in the controller:
def index
@accounts =
Account.all.map do |account|
account =
account.attributes.merge(
:logo_url =>
account.logo.attached? ?
polymorphic_url(account.logo) : nil
)
end
render :json => @accounts
end
This will return:
[
{
"id": 1,
"name": "Example1",
"logo_url": "/path/to/logo.jpg"
},
{
"id": 2,
"name": "Example2",
"logo_url": "/path/to/logo.jpg"
}
]
However, I don't want to do this whenever I call Account.all
; I want to automatically add the logo_url
whenever Account.all
is called.
I have come up with this solution; instead of calling it in the controller, I decided to override the method in the model. In app/models/account.rb
, I added:
def self.all
super.map do |account|
account =
account.attributes.merge(
:logo_url =>
account.logo.attached? ?
polymorphic_url(account.logo) : nil
)
end
end
The code above works so well. The problem is that methods find
, find_by
, where
, and other methods stopped working. And if I remove the self.all
method from account.rb
, it works again.
The following codes:
Account.find_by :id => params[:id]
Account.find params[:id]
raise an error:
undefined method `where' for #<Array:0x00007fd09b68d670>
I believe this happens because self.all
returns an array instead of an active record.
How can I override the all
method in the model?
Upvotes: 0
Views: 1174
Reputation: 1795
I don't think you want to override Account.all
.
Instead you want to change the default serialization of your model.
Try this:
class Account < ApplicationRecord
has_one_attached :logo
private
def serializable_hash(options)
super(options).merge("logo_url" => logo_url)
end
def logo_url
logo.attached? ? polymorphic_url(account.logo) : nil
end
end
The controller ends up calling to_json
on your model and the will end up using the #serializable_hash
. Since to_son
takes options to set a root of the json, it's best to override serializable_hash
. Another approach is to create a separate serializer classes per model but this should be suitable for a simple case.
Upvotes: 1