Reputation: 643
Quick question here. Tried searching to no avail.
I'm working to get a RESTful API off the ground for my current project. I've found Rails to be an absolute darling for this purpose, so check another mark into rails' awesomeness!
However I've hit a snag while designing my current API response for /users which is a method that's supposed to return a JSON array of User objects:
My User model:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
has_many :favorites
has_many :reservations
has_many :venues, through: :favorites
has_many :venues, through: :reservations
belongs_to :gender
belongs_to :city
belongs_to :role
has_one :avatar
has_many :payments
has_one :payment_history
has_many :promotion_claims
has_many :social_identifiers
end
My routes:
Rails.application.routes.draw do
namespace :api, defaults: { format: :json } do
namespace :v1 do
devise_for :users
..........................
resources :roles
resources :genders
resources :cities
#Users method
get 'users', to: "users#index"
end
end
My controller:
class API::V1::UsersController < API::V1::ApplicationController
before_action :set_address, only: [:show, :edit, :update, :destroy]
# GET /users
# GET /users.json
def index
@users = User.all
render json: @users
end
end
My serializers:
class UserSerializer < ActiveModel::Serializer
attributes :id, :first_name, :last_name, :cellphone, :email, :birthday, :username
has_one :gender
has_one :city
has_one :role
end
class GenderSerializer < ActiveModel::Serializer
attributes :name
end
class CitySerializer < ActiveModel::Serializer
attributes :name
end
class RoleSerializer < ActiveModel::Serializer
attributes :name
end
This achieves a pretty satisfying result as far as what I get as a json response:
// GET http://localhost:3000/api/v1/users
[
{
"id": 1,
"first_name": "Richmond",
"last_name": "Huels",
"cellphone": "29 8244 9100",
"email": "[email protected]",
"birthday": "2011-02-23T19:24:00.151Z",
"username": "alba.ortiz",
"gender": {
"name": "Male"
},
"city": {
"name": "San Pedro Garza García"
},
"role": {
"name": "admin"
}
},
However, what I want my JSON response to be is something more like:
// 20160225162402
// GET http://localhost:3000/api/v1/users
[
{
"id": 1,
"first_name": "Richmond",
"last_name": "Huels",
"cellphone": "29 8244 9100",
"email": "[email protected]",
"birthday": "2011-02-23T19:24:00.151Z",
"username": "alba.ortiz",
"gender": "Male",
"city": "San Pedro Garza García",
"role": "rp"
},
I tried overriding the attributes method in UserSerializer and make it so I can do whatever I want with the json hash before returning it but it didn't work:
#attributes override to get the right format for the response
def attributes
data = super
data[:city] = data[:city][:name]
data[:gender] = data[:gender][:name]
data[:role] = data[:city][:name]
data
end
Is there any way to achieve what I need for my API?
Thank you rubyists, you rule and keep being amazing!
Upvotes: 3
Views: 1368
Reputation: 643
I figured it out:
I feel like it's a hacky approach since basically, I'm not using the serializers for the models that are related to my User model and I'm overriding the attributes method, but what I did is that I removed all relationships from my User serializer then I overrode attributes exactly as I wrote it before:
class UserSerializer < ActiveModel::Serializer
attributes :id, :first_name, :last_name, :gender, :cellphone, :city, :email, :birthday, :username, :role
def attributes
data = super
data[:gender] = data[:gender][:name]
data[:city] = data[:city][:name]
data[:role] = data[:role][:name]
data
end
end
This gets me the JSON response that I need perfectly:
EDIT: I found an even better way:
My serializer:
class UserSerializer < ActiveModel::Serializer
attributes :id, :first_name, :last_name, :gender, :cellphone, :city, :email, :birthday, :username, :role
def gender
return object.gender.name
end
def city
return object.city.name
end
def role
return object.role.name
end
end
A lot cleaner and more object-oriented in my opinion.
Make sure that all the other serializers are in place and that the models have the proper validations for their fields.
The results are exactly the same:
Upvotes: 2
Reputation: 3635
If you want to merge the result into single hash.
You can use flatten method, example.
s = [ 1, 2, 3 ] #=> [1, 2, 3]
t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In your case, after getting all the result and pass it to variable "data"
data.flatten
Upvotes: 0