如何用ActiveModel生成指定列且按order排序的嵌套资源JSON?
嘿,我明白你卡在这儿了——要把嵌套的pages、sections、fields按各自的order字段排序,还得输出只包含name和order的JSON结构,用ActiveModel搞不定递归排序是吧?别慌,我给你几个靠谱的解决方案,分两种常用场景来说:
方案一:用ActiveModel Serializers(AMS)优雅处理
如果你的项目在用AMS,那可以在每个序列化器里直接指定关联的排序规则,同时筛选需要的字段:
先写对应的序列化器
# app/serializers/page_serializer.rb class PageSerializer < ActiveModel::Serializer attributes :name, :order # 关联sections时按order排序,并用SectionSerializer处理 has_many :sections, serializer: SectionSerializer do object.sections.order(:order) end end # app/serializers/section_serializer.rb class SectionSerializer < ActiveModel::Serializer attributes :name, :order # 关联fields时按order排序,并用FieldSerializer处理 has_many :fields, serializer: FieldSerializer do object.fields.order(:order) end end # app/serializers/field_serializer.rb class FieldSerializer < ActiveModel::Serializer attributes :name, :order end
控制器里调用序列化器
def index # 先把pages本身按order排序 pages = Page.order(:order) render json: pages, each_serializer: PageSerializer end
这样一来,从pages到sections再到fields,每一层都会按order字段排序,而且只会返回你要的name和order两个字段。
方案二:不用序列化器,用Rails原生as_json搞定
如果不想引入AMS,直接用Rails自带的as_json就能实现,无非就是递归处理每一层的排序和字段筛选:
写法一:控制器里直接处理(适合简单场景)
def index pages = Page.order(:order).map do |page| # 先获取page的name和order,再合并排序后的sections page.as_json(only: [:name, :order]).merge( sections: page.sections.order(:order).map do |section| # 同理处理section,再合并排序后的fields section.as_json(only: [:name, :order]).merge( fields: section.fields.order(:order).as_json(only: [:name, :order]) ) end ) end render json: pages end
写法二:模型里封装方法(更优雅,复用性强)
在每个模型里写一个序列化方法,把排序和字段筛选逻辑封装起来:
# app/models/page.rb class Page < ApplicationRecord has_many :sections def sorted_serialized as_json(only: [:name, :order]).merge( sections: sections.order(:order).map(&:sorted_serialized) ) end end # app/models/section.rb class Section < ApplicationRecord belongs_to :page has_many :fields def sorted_serialized as_json(only: [:name, :order]).merge( fields: fields.order(:order).as_json(only: [:name, :order]) ) end end
然后控制器里调用就简单多了:
def index pages = Page.order(:order).map(&:sorted_serialized) render json: pages end
额外优化:设置全局默认排序(减少重复代码)
如果你希望每次获取关联数据时,都自动按order排序,不用每次写order(:order),可以在模型的关联定义里直接指定:
# app/models/page.rb class Page < ApplicationRecord has_many :sections, -> { order(:order) } end # app/models/section.rb class Section < ApplicationRecord belongs_to :page has_many :fields, -> { order(:order) } end
这样不管是用AMS还是原生as_json,都不用再手动加排序逻辑了,关联数据默认就是有序的,省心不少~
内容的提问来源于stack exchange,提问作者MGreenfield
相关产品推荐
相关产品推荐

