0

呈现我的问题的假想简化版本。想象一下,我有一个网站,用户可以用自己的品牌创建一个商店,并从产品目录中选择要显示在他们的商店。商店&产品有一个拥有和属于多(HABTM)的关系。每种产品都有自己的特定商店路线。访问ActiveModelSerializers中Child序列化程序中的父对象

Rails.application.routes.draw do 
    resources :shops do 
    resources :products 
    end 
end 

class ShopSerializer < ActiveModel::Serializer 
    has_many :products 
end 

class ProductSerializer < ActiveModel::Serializer 
    include Rails.application.routes.url_helpers 

    attribute :url do 
    shop_product_url(NEED SHOP ID, product_id: object.id) 
    end 
end 

当一个店铺被序列化,并作为一个结果,所以是其产品的回收,我想产品串行要知道,是其序列的店铺,并用它来包含在路线序列化输出。这怎么可能?我试过所有从ShopSerializer传递instance_options的方式,但它不能按预期工作。

# this works except is apparently not threadsafe as multiple 
# concurrent requests lead to the wrong shop_id being used 
# in some of the serialized data 
has_many :products do 
    ActiveModelSerializers::SerializableResource.new(shop_id: object.id).serializable_hash 
end 

# shop_id isn't actually available in instance_options 
has_many :products do 
    ProductSerializer.new(shop_id: object.id) 
end 

回答

1

不幸的是,串行器关联似乎没有提供一种干净的方式将自定义属性传递给子序列化器。虽然有一些不那么漂亮的解决方案。

1.激活ProductSerializer手动,在ShopSerializer

class ProductSerializer < ActiveModel::Serializer 
end 

class ShopSerializer < ActiveModel::Serializer 
    include Rails.application.routes.url_helpers 

    attribute :products do 
    object.products.map do |product| 
     ProductSerializer.new(product).serializable_hash.merge(
     url: shop_product_url(object.id, product_id: product.id) 
    ) 
    end 
    end 
end 

2.添加店铺ID的Product实例添加URL它们被馈送到ProductSerializer

class ProductSerializer < ActiveModel::Serializer 
    include Rails.application.routes.url_helpers 

    attribute :url do 
    shop_product_url(object.shop_id, product_id: object.id) 
    end 
end 

class ShopSerializer < ActiveModel::Serializer 
    has_many :products, serializer: ProductSerializer do 
    shop = object 
    shop.products.map do |product| 
     product.dup.tap do |instance| 
     instance.singleton_class.send :define_method, :shop_id do 
      shop.id 
     end 
     end 
    end 
    end 
end 
之前

这两个解决方案tions应该是线程安全的,但第一种解决方案对我来说似乎是一个更好的主意,因为第二种解决方案使ProductSerializer自己无法使用—,即当只有一个Product在不知道它应该属于的特定商店的情况下被串行化。

+0

解决方案#1伟大的工作(没有尝试#2),只是我不得不打电话'Rails.application.routes.url_helpers.shop_product_url'否则我'NoMethodError(未定义的方法 'shop_product_url' 为#<::加载ActiveModel Serializer :: HasManyReflection:0x007fde59072670>)(甚至在串行器中包含Rails.application.routes.url_helpers) – swrobel

+1

我敢打赌,block是'instance_eval'ed,这就是为什么它不能访问包含的帮助器。您可以尝试在顶部使用'url_helpers = Rails.application.routes.url_helpers',并在模块内部使用'url_helpers.shop_product_url(...)',以获得更干净的解决方案。 –

+0

不幸的是,这实际上并不是线程安全的。我希望我对此有一些很好的见解,但实质上问题是,如果在不同商店的不同Puma线程上同时发出两个请求,第二个请求将使用第一个'object.id'又名'Shop#id' 。我无法写出失败的测试,但我在生产中可靠地看到它。尚未测试第二个解决方案... – swrobel