如何在Rails中对原生SQL查询结果使用ActiveModelSerializers序列化
原生SQL查询结果使用ActiveModelSerializers序列化的解决方案
问题根因
AMS(ActiveModelSerializers)默认仅支持实现了ActiveModel属性接口的对象序列化,你用到的两种返回值都不符合要求:
PG::Result:PostgreSQL驱动返回的原生结果集,仅提供基础的遍历、字段读取方法ActiveRecord::Result:Rails封装的结果集对象,本质是列名+行哈希的组合结构,未实现AMS要求的read_attribute_for_serialization属性读取接口
可行方案
方案1:结果转ActiveModel兼容实例(无对应模型时用)
先把每一行结果转成支持属性访问的对象,推荐两种实现:
- 轻量快速实现,用
OpenStruct封装:
sql = "SELECT * FROM table" results = ActiveRecord::Base.connection.exec_query(sql) # 逐行封装为OpenStruct,支持.访问属性 serializable_records = results.map { |row| OpenStruct.new(row) } options = { each_serializer: MySerializer } output = ActiveModelSerializers::SerializableResource.new(serializable_records, options).as_json
- 自定义ActiveModel类(需要扩展方法、性能要求高时用):
# 自定义轻量模型类 class SqlQueryResult include ActiveModel::Model # 动态绑定所有查询列的访问权限,也可以手动指定attr_accessor :id, :col1... attr_accessor(*ActiveRecord::Base.connection.exec_query("SELECT * FROM table LIMIT 0").columns) end # 调用时直接实例化 serializable_records = results.map { |row| SqlQueryResult.new(row) }
方案2:用find_by_sql查询(查询内容有对应ActiveRecord模型时用)
如果你的SQL查询的表已经有对应的ActiveRecord模型,直接用模型的find_by_sql方法,返回的结果就是模型实例,天生支持AMS序列化:
# 假设table对应的模型为Table results = Table.find_by_sql("SELECT * FROM table") options = { each_serializer: MySerializer } output = ActiveModelSerializers::SerializableResource.new(results, options).as_json
注:该方法即使SQL只查询部分字段也可正常运行,AMS只会读取序列化器中声明的字段,不会触发额外查询。
内容的提问来源于stack exchange,提问作者ninja
相关产品推荐
相关产品推荐

