Ruby on rails Rails:使用活动的\u模型\u序列化程序序列化深度嵌套的关联
我使用的是Ruby on rails Rails:使用活动的\u模型\u序列化程序序列化深度嵌套的关联,ruby-on-rails,json,active-model-serializers,Ruby On Rails,Json,Active Model Serializers,我使用的是rails4.2.1和active\u model\u序列化程序0.10.0.rc2 我是API新手,选择了active\u model\u序列化程序,因为它似乎正在成为rails的标准(尽管我并不反对使用RABL或其他序列化程序) 我遇到的问题是,我似乎无法在多级关系中包含各种属性。例如,我有: 项目 class ProjectSerializer < ActiveModel::Serializer attributes :id,
rails4.2.1
和active\u model\u序列化程序0.10.0.rc2
我是API新手,选择了active\u model\u序列化程序
,因为它似乎正在成为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
然而,我想让它产生的是:
{
"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的问题,似乎在如何处理这一问题上有一些反复(或者如果这种功能实际上得到了支持),但我很难准确地确定当前的状态
无论如何,如果您能举一个关于如何处理此问题或一般API建议的示例,我们将不胜感激。这将满足您的需求
@project.to_json(包括:{估计:{
包括:{:项目,:项目\代码,:税务\类型,:建议书}}})
顶级嵌套将自动包括在内,但任何比它更深的内容都需要包括在您的show action中或您称之为的任何地方。因此,这不是最好的答案,甚至不是一个好答案,但这正是我所需要的 在AMS中使用
json\u api
适配器时,似乎支持包含嵌套和侧加载属性,但我需要对平面json的支持。此外,该方法工作得很好,因为每个序列化程序都专门生成我所需要的内容,以独立于任何其他序列化程序,而无需在控制器中执行任何操作
欢迎提出意见/备选方法
项目模型
class Project < ActiveRecord::Base
has_many :estimates, autosave: true, dependent: :destroy
end
module Baserecord
class School < ApplicationRecord
has_many :programs, class_name: Baserecord.program_class, dependent: :destroy
has_many :faculties, class_name: Baserecord.faculty_class, through: :programs, dependent: :destroy
end
module Baserecord
class Faculty < ApplicationRecord
belongs_to :program, class_name: Baserecord.program_class
has_many :departments, class_name: Baserecord.department_class, dependent: :destroy
has_many :program_of_studies, class_name: Baserecord.program_of_study_class, through: :departments,
dependent: :destroy
end
end
项目序列化程序
def index
@projects = Project.all
render json: @projects
end
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
module Baserecord
class SchoolSerializer
include JSONAPI::Serializer
attributes :id, :name, :code, :description, :school_logo, :motto, :address
attribute :programs do |object|
# Create an empty array
customized_programs = []
object.programs.each do |program|
# Assign object attributes (returns a hash)
custom_program = program.attributes
# Create custom nested and side-loaded attributes
custom_program[:faculties] = program.faculties
# Push the created custom nested and side-loaded attributes into the empty array
customized_programs.push(custom_program)
end
# Return the new array
customized_programs
end
cache_options store: Rails.cache, namespace: 'jsonapi-serializer', expires_in: 1.hour
end
end
我基本上忽略了在序列化程序中实现任何
有许多
或属于
关联的尝试,只是定制了行为。我使用slice
选择特定属性。希望会出现一个更优雅的解决方案。如果使用JSONAPI适配器,可以执行以下操作来呈现嵌套关系:
render json: @project, include: ['estimates', 'estimates.project_code', 'estimates.tax_type', 'estimates.proposals']
您可以阅读jsonapi文档中的更多内容:Per commit 1426:-和相关讨论,您可以看到json
和属性
序列化的默认嵌套是一个级别
如果默认情况下需要深度嵌套,可以在活动的\u model\u序列化程序初始值设定项中设置配置属性:
ActiveModelSerializers.config.default\u包含='**'
有关v0.10.6的详细参考信息:您可以更改
ActiveModel::Serializer的默认\u包含内容
:
# 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
在我的例子中,我创建了一个名为“active_model_serializer.rb”的文件,该文件位于“MyApp/config/initializers”处,包含以下内容:
ActiveModelSerializers.config.default_includes = '**'
不要忘记重新启动服务器:
$ rails s
为了进一步说明,我补充了以下答案
我在rails应用程序中使用了用于序列化的。我没有发现在控制器中包含嵌套属性和侧加载属性对我来说很方便。我只是想更好地分离关注点。因此,任何与序列化有关的内容都应该只在序列化文件中,而与控制器文件无关
因此,在我的案例中,我有以下联系:
学校模式
class Project < ActiveRecord::Base
has_many :estimates, autosave: true, dependent: :destroy
end
module Baserecord
class School < ApplicationRecord
has_many :programs, class_name: Baserecord.program_class, dependent: :destroy
has_many :faculties, class_name: Baserecord.faculty_class, through: :programs, dependent: :destroy
end
module Baserecord
class Faculty < ApplicationRecord
belongs_to :program, class_name: Baserecord.program_class
has_many :departments, class_name: Baserecord.department_class, dependent: :destroy
has_many :program_of_studies, class_name: Baserecord.program_of_study_class, through: :departments,
dependent: :destroy
end
end
就这些
我希望这会有所帮助这种方法是否真的利用了活动的(model)序列化程序
,或者它只是rails输出JSON
的默认方式?@greetification否,它使用了rails还原时留下的AMS痕迹。请参见Rails ActiveModel::Serializers::JSON和ActiveModel::Serialization,我想这个方法最大的问题是它需要在两个不同的文件中管理代码;控制器和序列化程序。这并不是说这不是正确的做法;只是我觉得更不方便。这是得到“筑巢”的最好方法。。虽然JSON:API实际上将其扁平化了。如果您不希望将深度嵌套作为默认值,但希望将其用于一个特定的模型,该怎么办?(如果特定模型需要2或3个嵌套级别怎么办?)您可以在每个操作中传入'**'
。请注意,这是一个安全问题。最好是明确你想要包括什么。虽然这个问题的最上等答案肯定是正确的传统方式,但当遵循AMS惯例时,很难得到你想要的。我也只是编写自己的回调来显示嵌套的关联,并且发现AMS约定太笨拙了。我发现几乎所有关于Rails和嵌套关联的事情都是这样的——约定很难处理,请遵循普通的ol'ruby。与您的问题无关,但请在序列化程序文件中使用该间距!它只会降低可读性,所以你知道,我个人觉得它更可读,但我认为每个人都有自己的可读性。
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