使用active_model_serializers序列化深层嵌套关联
我正在使用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
和估计
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问题,似乎有一些关于应该如何处理的问题(或者如果这种function甚至实际上得到支持),但是我很难搞清楚当前的情况国家是。
- https://github.com/rails-api/active_model_serializers/issues/835
- https://github.com/rails-api/active_model_serializers/issues/968
- https://github.com/rails-api/active_model_serializers/issues/414
- https://github.com/rails-api/active_model_serializers/issues/444
建议的解决方案之一是用一个方法来覆盖属性来调用嵌套属性,但这似乎被认为是一个黑客,所以我想尽可能避免它。
无论如何,非常感谢如何进行此API或一般API建议的示例。
根据提交1426: https : //github.com/rails-api/active_model_serializers/pull/1426 – 以及相关讨论,您可以看到json
和attributes
序列化的默认嵌套是一个级别。
如果您希望默认深度嵌套,可以在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
所以这不是最好的,甚至不是一个好的答案,但这是我需要它的工作方式。
虽然在使用带有AMS的json_api
适配器时似乎支持包含嵌套和侧载属性,但我需要支持flat 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
来选择特定的属性。 希望更优雅的解决方案即将推出。
在我的例子中,我在’MyApp / config / initializers’中创建了一个名为’active_model_serializer.rb’的文件,其中包含以下内容:
ActiveModelSerializers.config.default_includes = '**'
别忘了重启服务器:
$ rails s
您可以更改ActiveModel::Serializer
default_includes
:
# 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 } } } )
顶级嵌套将自动包含在内,但是任何比这更深的嵌套都需要包含在您的show动作中或您调用它的任何位置。
- 控制器中的索引方法不适用于活动模型序列化程序
- 具有嵌入式ID和侧载的Ember-Data和Active Model Serializer的has_many配置
- 更改活动模型序列化器默认适配器
- 使用ActiveModel :: Serializer序列化具有属性的数组/关系
- Ember Data属于协会(JSON格式?)
- 使用active_model_serializers序列化模型数组
- <NoMethodError:#Record :: ActiveRecord_Relation的未定义方法`read_attribute_for_serialization':
- 使用active_model_serializers序列化权限(例如CanCan)
- 活动模型序列化程序belongs_to