如何在Ruby on Rails 6.0.2中创建包含嵌套JSON对象的API响应?
嗨,我来帮你解决这个嵌套JSON响应的问题——完全不需要手动多次查询再拼接数据,Rails有一套顺畅的方案来处理这个需求,分几步走就行:
首先得保证你的模型之间的关联关系是对的,这样Rails才能正确识别并加载关联数据:
- 在
app/models/event.rb里添加:
class Event < ApplicationRecord belongs_to :artist belongs_to :country end
- 在
app/models/artist.rb里(可选,但建议加上方便反向查询):
class Artist < ApplicationRecord has_many :events end
- 在
app/models/country.rb里:
class Country < ApplicationRecord has_many :events end
直接查events后逐个调用.artist或.country会触发N+1次数据库查询(1次查events,每个event各查1次artist和country),性能很差。用includes可以一次性把关联数据都拉出来:
在app/controllers/events_controller.rb里,处理/events/artist/:artist_id路由的方法这么写:
def index_by_artist @events = Event.where(artist_id: params[:artist_id]).includes(:artist, :country) # 后续处理序列化返回 end
别忘了在config/routes.rb配置对应路由:
get '/events/artist/:artist_id', to: 'events#index_by_artist'
有三种常用方式生成你要的嵌套JSON格式,选顺手的就行:
方式一:用Rails内置的as_json(快速简单)
直接在控制器里用as_json指定要包含的关联,还能自定义返回字段:
def index_by_artist @events = Event.where(artist_id: params[:artist_id]).includes(:artist, :country) render json: @events.as_json( only: [:id, :type], include: { artist: { only: [:id, :name] }, country: { only: [:id, :name] } } ) end
这样返回的JSON结构完全符合你给出的示例。
方式二:用Jbuilder(自定义性强)
如果需要精细控制JSON结构,推荐用Jbuilder。创建视图文件app/views/events/index_by_artist.json.jbuilder,写入:
json.array! @events do |event| json.id event.id json.type event.type json.artist do json.id event.artist.id json.name event.artist.name end json.country do json.id event.country.id json.name event.country.name end end
控制器里只需:
def index_by_artist @events = Event.where(artist_id: params[:artist_id]).includes(:artist, :country) render :index_by_artist end
这种方式适合后续需要调整字段的场景,灵活性拉满。
方式三:用Active Model Serializers(结构化强)
如果API有大量序列化需求,Active Model Serializers(AMS)是更规范的选择。先把gem加进Gemfile:
gem 'active_model_serializers'
运行bundle install后,创建三个序列化器:
app/serializers/event_serializer.rb:
class EventSerializer < ActiveModel::Serializer attributes :id, :type has_one :artist has_one :country end
app/serializers/artist_serializer.rb:
class ArtistSerializer < ActiveModel::Serializer attributes :id, :name end
app/serializers/country_serializer.rb:
class CountrySerializer < ActiveModel::Serializer attributes :id, :name end
最后控制器直接返回:
def index_by_artist @events = Event.where(artist_id: params[:artist_id]).includes(:artist, :country) render json: @events end
AMS会自动生成符合要求的嵌套JSON结构。
核心就是利用Rails的关联预加载(解决性能问题)和序列化工具(生成嵌套结构),完全不需要手动拼接数据。三种方式都能满足你的需求,根据场景选择就行~
内容的提问来源于stack exchange,提问作者justUrAverageProgrammer

