Rails is a powerful Ruby framework that allows developers to focus on developing while handling most of the tedium work, i.e. database querying, in the background as long as you, the developer, follow the "convention-over-configuration" format. Rails' generators make following that format even easier.
For my current project, we want a User to be able to see all of their upcoming events. There a few ways to accomplish this and luckily Rails offers a couple simple solutions to us.
Note: Rails' conventions rely on the resource associations, In my previous blog I cover Active Record associations and some tips to help set them up correctly.
USING INCLUDE: & EXCEPT:
When we query the database to find a User, we want to receive the User's information along with the events associated with that User. By default, the show
action in the Users Controller will only return the User data:
#users_controller.rb
def show
user = User.find(params[:id])
render json: user
end
---
#server response
{
"id": 2,
"user": "kevin"
"created_at": "2022-12-08T07:03:38.943Z",
"updated_at": "2022-12-08T07:03:38.943Z",
}
If we add include: :events
as part of the show
action's render we will receive all events associated with that user.
#users_controller.rb
def show
user = User.find(params[:id])
render json: user, include: :events
end
---
#server response
{
"id": 2,
"user": "kevin",
"created_at": "2022-12-08T07:03:38.943Z",
"updated_at": "2022-12-08T07:03:38.943Z",
"events": [
{
"id": 9,
"name": "Muse",
"venue": "Houston Center",
"event_type": "concert",
"created_at": "2022-12-08T06:09:39.617Z",
"updated_at": "2022-12-08T06:09:39.617Z"
},
{
"id": 5,
"name": "Radiohead",
"venue": "Atlanta-Center",
"event_type": "concert",
"created_at": "2022-12-08T06:09:39.596Z",
"updated_at": "2022-12-08T06:09:39.596Z"
}
]
}
Rails generators will, by default, add t.timestamps
columns to your tables. We can use except:
to prevent them from being returned by the server, however this will only exclude those keys from the top level resource, our events will still show those key/value pairs.
#users_controller.rb
def show
user = User.find(params[:id])
render json: user, except: [:created_at, :updated_at], include: :events
end
---
#server response
{
"id": 2,
"user": "kevin",
"events": [
{
"id": 9,
"name": "Muse",
"venue": "Houston Center",
"event_type": "concert",
"created_at": "2022-12-08T06:09:39.617Z",
"updated_at": "2022-12-08T06:09:39.617Z"
},
{
"id": 5,
"name": "Radiohead",
"venue": "Atlanta-Center",
"event_type": "concert",
"created_at": "2022-12-08T06:09:39.596Z",
"updated_at": "2022-12-08T06:09:39.596Z"
}
]
}
We could work around that by nesting include:
and except:
statements, however, even with this simple example, that becomes very messy:
def show
user = User.find(params[:id])
render json: user.to_json(except: [:created_at, :updated_at], include: [:events => {except:[:created_at, :updated_at]}])
end
---
#server response
{
"id": 2,
"user": "kevin",
"events": [
{
"id": 9,
"name": "Muse",
"venue": "Houston Center",
"event_type": "concert",
},
{
"id": 5,
"name": "Radiohead",
"venue": "Atlanta-Center",
"event_type": "concert",
}
]
}
If you really wanted to, you can keep chaining include:
and except:
as much as needed but Rails provides a better way.
SERIALIZERS
Using Rails serializers allows us to clean up the Users Controller and control what data is being returned by the server. You will have to make sure the gem is added to your Gemfile and installed. This gem is not included when running rails new
gem 'active_model_serializers'
Once installed, you can use rails g resource
to create a model, controller and serializer at the same time, or you can also use rails g serializer <name(singular)>
if you already have a model & controller.
Serialzers user attributes
to define what keys are returned instead of only:
or except:
. They also make use of Active Record associations to include data from other tables.
Returning to our User events example, we can simplify the Users Controller and add attributes
and the necessary relationships to our serializers.
#users_controller.rb
def show
user = User.find(params[:id])
render json: user
end
---
#user_serializer.rb
class UserSerializer < ActiveModel::Serializer
attributes :id, :username
has_many :events
end
---
#event_serializer.rb
class EventSerializer < ActiveModel::Serializer
attributes :id, :name, :venue, :event_type
end
After updating all the files, our server response will be:
#server response
{
"id": 2,
"user": "kevin",
"events": [
{
"id": 9,
"name": "Muse",
"venue": "Houston Center",
"event_type": "concert",
},
{
"id": 5,
"name": "Radiohead",
"venue": "Atlanta-Center",
"event_type": "concert",
}
]
}
Using serializers we can make our code cleaner and more efficient by following "separation of concerns" and Rails "convention over configuration". This is a simple example showing the basics of Rails Active Model Serializers
Further reading:
Active Model Serializers Github
Top comments (0)