Ruby-on-rails 使用 active_model_serializers 序列化深度嵌套的关联
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32079897/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Serializing deeply nested associations with active_model_serializers
提问by Eric Norcross
I'm using Rails 4.2.1and active_model_serializers 0.10.0.rc2
我正在使用Rails 4.2.1和active_model_serializers 0.10.0.rc2
I'm new to API's and chose active_model_serializersbecause it seems to be becoming the standard for rails (Although I'm not opposed to using RABLor another serializer)
我是 API 的新手并选择active_model_serializers它是因为它似乎正在成为 Rails 的标准(尽管我不反对使用RABL或其他序列化程序)
The problem I'm having is that I can't seem to include various attributes in multi-level relationships. For instance I have:
我遇到的问题是我似乎无法在多级关系中包含各种属性。例如我有:
Projects
项目
class ProjectSerializer < ActiveModel::Serializer
attributes :id,
:name,
:updated_at
has_many :estimates, include_nested_associations: true
end
and 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
Proposals
提案
class ProposalSerializer < ActiveModel::Serializer
attributes :id,
:name,
:updated_at,
:estimate_id
belongs_to :estimate
end
When I hit the /projects/1the above produces:
当我击中/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
}
]
}
However, what I'd like it to produce is:
但是,我希望它产生的是:
{
"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"
}
]
}
]
}
Ideally, I'd also like to be able to specify which attributes, associations, and attributes of those associations that are included in each serializer.
理想情况下,我还希望能够指定每个序列化程序中包含哪些属性、关联和这些关联的属性。
I've been looking through the AMS issues, and there does seem to be some back and forth on how this should be handled (or if this kind of functionality is even actually supported) but I'm having difficulty figuring out exactly what the current state is.
我一直在查看 AMS 问题,并且似乎在如何处理这方面存在一些来回(或者是否实际上支持这种功能),但我很难弄清楚当前到底是什么状态是。
- 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
- 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
One of the proposed solutions was to override the attribute with a method to call the nested attributes, but that seems to be regarded as a hack so I wanted to avoid it if possible.
建议的解决方案之一是使用调用嵌套属性的方法覆盖该属性,但这似乎被视为一种黑客行为,因此我想尽可能避免它。
Anyway, an example of what of how to go about this or general API advice would be much appreciated.
无论如何,非常感谢如何处理此或一般 API 建议的示例。
采纳答案by Eric Norcross
So this my not be the best or even a good answer, but this is working how I need it to.
所以这不是最好的,甚至不是一个好的答案,但这是我需要的。
While including nested and side-loaded attributes appears to be supported when using the json_apiadapter with AMS, I needed support for flat json. In addition, this method worked well because each serializer is specifically generating exactly what I need it to independent of any other serializer and without having to do anything in the controller.
虽然在将json_api适配器与 AMS 一起使用时似乎支持包含嵌套和侧载属性,但我需要支持平面 json。此外,这种方法运行良好,因为每个序列化器都专门生成我需要的内容,独立于任何其他序列化器,而无需在控制器中执行任何操作。
Comments / alternate methods are always welcome.
始终欢迎评论/替代方法。
Project Model
项目模型
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
Result
结果
[
{
"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"
}
]
}
]
}
]
I basically disregarded trying to implement any has_manyor belongs_toassociations in the serializers and just customized the behavior. I used sliceto select specific attributes. Hopefully a more elegant solution will be forth coming.
我基本上没有尝试在序列化程序中实现任何has_many或belongs_to关联,而只是自定义了行为。我曾经slice选择特定的属性。希望一个更优雅的解决方案即将到来。
回答by Daniel D
Per commit 1426: https://github.com/rails-api/active_model_serializers/pull/1426- and related discussion, you can see that the default nesting for jsonand attributesserialization is one level.
每个提交 1426:https: //github.com/rails-api/active_model_serializers/pull/1426- 和相关讨论,您可以看到默认嵌套json和attributes序列化是一级。
If you want deep nesting by default, you can set a configuration property in an active_model_serializer initializer:
如果你想要默认深度嵌套,你可以在 active_model_serializer 初始值设定项中设置一个配置属性:
ActiveModelSerializers.config.default_includes = '**'
ActiveModelSerializers.config.default_includes = '**'
For detailed reference from v0.10.6: https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/adapters.md#include-option
有关v0.10.6 的详细参考:https: //github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/adapters.md#include-option
回答by Brandon Patram
If you are using the JSONAPI adapter you can do the following to render nested relationships:
如果您使用 JSONAPI 适配器,您可以执行以下操作来呈现嵌套关系:
render json: @project, include: ['estimates', 'estimates.project_code', 'estimates.tax_type', 'estimates.proposals']
You can read more from the jsonapi documentation:http://jsonapi.org/format/#fetching-includes
您可以从 jsonapi 文档中阅读更多内容:http://jsonapi.org/format/#fetching-includes
回答by blackchestnut
You can change default_includesfor the ActiveModel::Serializer:
您可以更改default_includes为ActiveModel::Serializer:
# config/initializers/active_model_serializer.rb
ActiveModel::Serializer.config.default_includes = '**' # (default '*')
In addition, in order to avoid infinite recursion, you can control the nested serialization follows:
另外,为了避免无限递归,可以控制嵌套序列化如下:
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
Result:
结果:
{
"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"
}
]
}
]
}
]
}
}
回答by Javier Calatrava Llavería
In my case I created a file called 'active_model_serializer.rb' placed at 'MyApp/config/initializers' with the following content:
在我的例子中,我创建了一个名为“active_model_serializer.rb”的文件,放置在“MyApp/config/initializers”中,内容如下:
ActiveModelSerializers.config.default_includes = '**'
Do not forget to restart the server:
不要忘记重新启动服务器:
$ rails s
回答by Colton Fent
This should do what you're looking for.
这应该做你正在寻找的。
@project.to_json( include: { estimates: {
include: {:project, :project_code, :tax_type, :proposals } } } )
@project.to_json( include: { estimates: {
include: {:project, :project_code, :tax_type, :proposals } } } )
The top level nesting will be automatically included, but anything deeper than that will need to be included in your show action or wherever you are calling this.
顶级嵌套将自动包含在内,但任何比这更深的嵌套都需要包含在您的显示操作中或您调用它的任何地方。


