Rails 4.2.1
とactive_model_serializers 0.10.0.rc2
を使用しています
私はAPIを初めて使用し、active_model_serializers
を選択しました。Railsの標準になりつつあるようです(ただし、RABL
または別のシリアライザーを使用することに反対しているわけではありません)
私が抱えている問題は、マルチレベルの関係にさまざまな属性を含めることができないように見えることです。例えば、私は持っています:
プロジェクト
class ProjectSerializer < ActiveModel::Serializer
attributes :id,
:name,
:updated_at
has_many :estimates, include_nested_associations: true
end
およびEstimates
class EstimateSerializer < ActiveModel::Serializer
attributes :id,
:name,
:release_version,
:exchange_rate,
:updated_at,
:project_id,
:project_code_id,
:tax_type_id
belongs_to :project
belongs_to :project_code
belongs_to :tax_type
has_many :proposals
end
提案
class ProposalSerializer < ActiveModel::Serializer
attributes :id,
:name,
:updated_at,
:estimate_id
belongs_to :estimate
end
/projects/1
をヒットすると、上記の結果が生成されます。
{
"id": 1,
"name": "123 Park Ave.",
"updated_at": "2015-08-09T02:36:23.950Z",
"estimates": [
{
"id": 1,
"name": "E1",
"release_version": "v1.0",
"exchange_rate": "0.0",
"updated_at": "2015-08-12T04:23:38.183Z",
"project_id": 1,
"project_code_id": 8,
"tax_type_id": 1
}
]
}
しかし、私がそれを作りたいのは:
{
"id": 1,
"name": "123 Park Ave.",
"updated_at": "2015-08-09T02:36:23.950Z",
"estimates": [
{
"id": 1,
"name": "E1",
"release_version": "v1.0",
"exchange_rate": "0.0",
"updated_at": "2015-08-12T04:23:38.183Z",
"project": {
"id": 1,
"name": "123 Park Ave."
},
"project_code": {
"id": 8,
"valuation": 30
},
"tax_type": {
"id": 1,
"name": "no-tax"
},
"proposals": [
{
"id": 1,
"name": "P1",
"updated_at": "2015-08-12T04:23:38.183Z"
},
{
"id": 2,
"name": "P2",
"updated_at": "2015-10-12T04:23:38.183Z"
}
]
}
]
}
また、理想的には、各シリアライザーに含まれる属性、関連付け、およびそれらの関連付けの属性を指定できるようにしたいと思います。
私はAMSの問題を調べてきましたが、これをどのように扱うべきか(またはこの種の機能が実際にサポートされているかどうか)についていくつかのやり取りがあるようですが、現在の状況を正確に把握するのは困難です状態です。
提案された解決策の1つは、ネストされた属性を呼び出すメソッドで属性をオーバーライドすることでしたが、それはハックと見なされるようなので、可能であればそれを避けたいと思いました。
とにかく、このアドバイスや一般的なAPIアドバイスをどのように実行するかの例は大歓迎です。
だから、これは最良の答えでも良い答えでもありませんが、これは私がそれを必要とする方法で機能しています。
ネストされた属性とサイドロードされた属性を含めると、json_api
AMS付きアダプター、フラットjsonのサポートが必要でした。さらに、このメソッドは、各シリアライザーが他のシリアライザーから独立して、コントローラーで何もすることなく必要なものを正確に生成するため、うまく機能しました。
コメント/代替方法はいつでも歓迎します。
プロジェクトモデル
class Project < ActiveRecord::Base
has_many :estimates, autosave: true, dependent: :destroy
end
ProjectsController
def index
@projects = Project.all
render json: @projects
end
ProjectSerializer
class ProjectSerializer < ActiveModel::Serializer
attributes :id,
:name,
:updated_at,
# has_many
:estimates
def estimates
customized_estimates = []
object.estimates.each do |estimate|
# Assign object attributes (returns a hash)
# ===========================================================
custom_estimate = estimate.attributes
# Custom nested and side-loaded attributes
# ===========================================================
# belongs_to
custom_estimate[:project] = estimate.project.slice(:id, :name) # get only :id and :name for the project
custom_estimate[:project_code] = estimate.project_code
custom_estimate[:tax_type] = estimate.tax_type
# has_many w/only specified attributes
custom_estimate[:proposals] = estimate.proposals.collect{|proposal| proposal.slice(:id, :name, :updated_at)}
# ===========================================================
customized_estimates.Push(custom_estimate)
end
return customized_estimates
end
end
結果
[
{
"id": 1,
"name": "123 Park Ave.",
"updated_at": "2015-08-09T02:36:23.950Z",
"estimates": [
{
"id": 1,
"name": "E1",
"release_version": "v1.0",
"exchange_rate": "0.0",
"created_at": "2015-08-12T04:23:38.183Z",
"updated_at": "2015-08-12T04:23:38.183Z",
"project": {
"id": 1,
"name": "123 Park Ave."
},
"project_code": {
"id": 8,
"valuation": 30,
"created_at": "2015-08-09T18:02:42.079Z",
"updated_at": "2015-08-09T18:02:42.079Z"
},
"tax_type": {
"id": 1,
"name": "No Tax",
"created_at": "2015-08-09T18:02:42.079Z",
"updated_at": "2015-08-09T18:02:42.079Z"
},
"proposals": [
{
"id": 1,
"name": "P1",
"updated_at": "2015-08-12T04:23:38.183Z"
},
{
"id": 2,
"name": "P2",
"updated_at": "2015-10-12T04:23:38.183Z"
}
]
}
]
}
]
私は基本的にhas_many
またはbelongs_to
シリアライザーの関連付けと、動作のカスタマイズ。 slice
を使用して特定の属性を選択しました。よりエレガントなソリューションが近々登場することを願っています。
コミット1426ごと: https://github.com/Rails-api/active_model_serializers/pull/1426 -および関連する説明から、json
およびattributes
シリアル化のデフォルトのネストは1レベルであることがわかります。
デフォルトで深いネストが必要な場合は、active_model_serializerイニシャライザーで構成プロパティを設定できます。
ActiveModelSerializers.config.default_includes = '**'
v0.10.6からの詳細なリファレンス: https://github.com/Rails-api/active_model_serializers/blob/v0.10.6/ docs/general/adapters.md#include-option
JSONAPIアダプターを使用している場合、以下を実行してネストされた関係をレンダリングできます。
render json: @project, include: ['estimates', 'estimates.project_code', 'estimates.tax_type', 'estimates.proposals']
Jsonapiドキュメントから詳細を読むことができます: http://jsonapi.org/format/#fetching-includes
あなたは変えられる default_includes
のために ActiveModel::Serializer
:
# config/initializers/active_model_serializer.rb
ActiveModel::Serializer.config.default_includes = '**' # (default '*')
さらに、無限の再帰を回避するために、次のようにネストされたシリアル化を制御できます。
class UserSerializer < ActiveModel::Serializer
include Rails.application.routes.url_helpers
attributes :id, :phone_number, :links, :current_team_id
# Using serializer from app/serializers/profile_serializer.rb
has_one :profile
# Using serializer described below:
# UserSerializer::TeamSerializer
has_many :teams
def links
{
self: user_path(object.id),
api: api_v1_user_path(id: object.id, format: :json)
}
end
def current_team_id
object.teams&.first&.id
end
class TeamSerializer < ActiveModel::Serializer
attributes :id, :name, :image_url, :user_id
# Using serializer described below:
# UserSerializer::TeamSerializer::GameSerializer
has_many :games
class GameSerializer < ActiveModel::Serializer
attributes :id, :kind, :address, :date_at
# Using serializer from app/serializers/gamers_serializer.rb
has_many :gamers
end
end
end
結果:
{
"user":{
"id":1,
"phone_number":"79202700000",
"links":{
"self":"/users/1",
"api":"/api/v1/users/1.json"
},
"current_team_id":1,
"profile":{
"id":1,
"name":"Alexander Kalinichev",
"username":"Blackchestnut",
"birthday_on":"1982-11-19",
"avatar_url":null
},
"teams":[
{
"id":1,
"name":"Agile Season",
"image_url":null,
"user_id":1,
"games":[
{
"id":13,
"kind":"training",
"address":"",
"date_at":"2016-12-21T10:05:00.000Z",
"gamers":[
{
"id":17,
"user_id":1,
"game_id":13,
"line":1,
"created_at":"2016-11-21T10:05:54.653Z",
"updated_at":"2016-11-21T10:05:54.653Z"
}
]
}
]
}
]
}
}
これはあなたが探していることをするはずです。
@project.to_json( include: { estimates: { include: {:project, :project_code, :tax_type, :proposals } } } )
最上位のネストは自動的に含まれますが、それよりも深いものは、ショーアクションまたはこれを呼び出す場所に含める必要があります。