我有一个Rails 4 API。当用户在视图中搜索船时,将执行此方法,以获取与搜索过滤器匹配的所有船,并使用render ActiveModel和:include和:only将船模型数组作为json返回:
render :json => @boats, :include => { :mainPhoto => {:only => [:name, :mime]},
:year => {:only => :name},
# other includes...}
这很好。
但是,除此信息外,我想显示的船只总数为“显示1-20条 80 船只”,因为它具有分页功能。因此,关键是我需要提供 80 船。我想避免发送两个执行几乎相同逻辑的请求,所以这个想法是只运行一次searchBoats方法,并在结果中提供船列表以及变量numTotalBoats中的船总数。我了解numTotalBoats不是船模型属性。因此,我认为它应该在渲染结果中包含一个自变量。 像这样:
render :json => {boats: @boats with all the includes, numTotalBoats: @NumTotalBoats}
我尝试了数千种组合,但是或者我遇到语法错误,或者没有一个返回预期的结果,类似
{boats: [boat1 with all the includes, boat2 with all the includes, ... boatN with all the includes], numTotalBoats: N}
答案 0 :(得分:1)
不添加任何宝石:
def index
boats = Boat.includes(:year)
render json: {
boats: boats.as_json(include: { year: { only: :name } }),
numTotalBoats: boats.count
}
end
尽管如此,我相信您应该使用独立的序列化程序:
注意:根据您是否使用分页宝石,可能需要将下面的.count
调用更改为.total_count
(对于Kaminari),将从分页集合中正确读取计数。
我建议使用ActiveModel Serializers,这将根据您的情况完成。
首先将gem添加到Gemfile中:
gem 'active_model_serializers', '~-> 0.10'
在config / initializers / active_model_serializer.rb中覆盖适配器:
ActiveModelSerializers.config.adapter = :json
为模型定义序列化器,
# app/serializers/boat_serializer.rb
class BoatSerializer < ActiveModel::Serializer
attributes :name
has_one :year
end
# app/serializers/year_serializer.rb
class YearSerializer < ActiveModel::Serializer
attributes :name
end
最后,在您的控制器中:
boats = Boat.includes(:year)
render json: boats, meta: boats.count, meta_key: "numTotalBoats"
您将实现:
{
"boats": [
{
"name": "Boaty McBoatFace",
"year": {
"name": "2018"
}
},
{
"name": "Titanic",
"year": {
"name": "1911"
}
}
],
"numTotalBoats": 2
}
在每个索引控制器中添加该计数有点乏味,因此我通常最终要定义自己的适配器或集合序列化器,以便自动进行处理(在Rails 5而不是4上进行了测试)。
# lib/active_model_serializers/adapter/json_extended.rb
module ActiveModelSerializers
module Adapter
class JsonExtended < Json
def meta
if serializer.object.is_a?(ActiveRecord::Relation)
{ total_count: serializer.object.count }
end.to_h.merge(instance_options.fetch(:meta, {})).presence
end
end
end
end
# config/initializers/active_model_serializer.rb
ActiveModelSerializers.config.adapter = ActiveModelSerializers::Adapter::JsonExtended
# make sure to add lib to eager load paths
# config/application.rb
config.eager_load_paths << Rails.root.join("lib")
现在您的索引操作可以如下所示
def index
boats = Boat.includes(:year)
render json: boats
end
并输出:
{
"boats": [
{
"name": "Boaty McBoatFace",
"year": {
"name": "2018"
}
},
{
"name": "Titanic",
"year": {
"name": "1911"
}
}
],
"meta": {
"total_count": 2
}
}
我认为为不同的端点解析此计数要容易一些,并且您在响应集合时会自动获得该计数,因此您的控制器会更简单。