【问题标题】:Accessing Parent object in Child serializer in ActiveModelSerializers在 ActiveModelSerializers 的子序列化程序中访问父对象
【发布时间】:2017-07-08 17:41:32
【问题描述】:

提出我的问题的假设简化版本。想象一下,我有一个网站,用户可以在其中创建一个带有自己品牌的商店,并从产品目录中选择要在他们的商店中显示的产品。商店和产品具有多属性 (HABTM) 关系。每个产品都有自己的商店特定路线。

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

当 Shop 被序列化时,其产品的集合也是如此,我希望 Product 序列化程序知道正在对其进行序列化的商店,并使用它在序列化输出中包含路线。这怎么可能?我已经尝试了从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

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-5 active-model-serializers


    【解决方案1】:

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

    1.手动调用ProductSerializer,在ShopSerializer中添加URL

    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 实例,然后再将它们馈送到 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
    

    两个解决方案都应该是线程安全的,但第一个解决方案对我来说似乎是一个更好的主意,因为第二个解决方案使 ProductSerializer 无法单独使用 — 即在不知道特定商店的情况下仅序列化一个 Product它应该属于。

    【讨论】:

    • 解决方案#1 效果很好(没有尝试#2),除了我必须调用Rails.application.routes.url_helpers.shop_product_url 否则我得到NoMethodError (undefined method 'shop_product_url' for #&lt;ActiveModel::Serializer::HasManyReflection:0x007fde59072670&gt;)(即使在序列化程序中使用include Rails.application.routes.url_helpers
    • 我敢打赌该块是instance_evaled,这就是它无法访问包含的帮助程序的原因。您可以在顶部尝试url_helpers = Rails.application.routes.url_helpers,在块内尝试url_helpers.shop_product_url(...),以获得更清洁的解决方案。
    • 不幸的是,这实际上似乎不是线程安全的。我希望我对原因有一些很好的了解,但本质上问题是,如果不同商店的不同 Puma 线程上有两个同时请求,第二个请求将使用第一个的 object.id aka Shop#id。我无法编写失败的测试,但我在生产中可靠地看到了它。还没有测试第二个解决方案...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-17
    • 1970-01-01
    • 1970-01-01
    • 2019-05-09
    相关资源
    最近更新 更多