【问题标题】:Preload Ecto Association on Insert插入时预加载 Ecto 关联
【发布时间】:2018-01-29 17:50:03
【问题描述】:

对于多租户电子商务应用程序,我给出了以下 ER 架构:

Order has_many OrderProduct belongs_to Product belongs_to Price

使用 Ecto,很容易通过 Order 的变更集函数中的 cast_assoc(:order_product) 一次插入具有多个 OrderProductOrder .

但由于 ProductPrice 将来可能会被删除,我想另外存储 Price 的值和OrderProducts 中的数量(:price:quantity)。

据我所知,我有以下选择:

  1. OrderProduct 的变更集函数中,我可以获取 Product 并通过 Product 的 ID 加入 Price 和将 Price:price 添加为 OrderProduct 的更改。这将导致 N+1 个查询,因为我必须为 Order 中的每个 OrderProduct 执行该查询(在这种情况下可以,因为我只有有限的数量每个订单产品)。但此外,我更愿意保留模式定义模块的 Repo。但最重要的是:我在多租户设置中使用了不同的架构,因此我需要知道 OrderProduct 的变更集函数中的 prefix 才能在正确的 prefix 上执行查询。
  2. 在我的 Orders 模块中的架构定义之外迭代 OrderProducts 并将 :price 添加为 OrderProduct 变更集的更改:
%Order{} 
|> Order.changeset(attrs)
|> iterate_order_products_and_map_price_change(tenant)
|> Repo.insert(prefix: tenant)
  1. 使用Ecto.Multi 先插入订单,然后我用额外的Ecto.Multi.run 添加关系

我更喜欢第一个选项,因为在我看来这是最干净的方法。

  • 对于这种情况还有其他或更好的选择吗?
  • 是否也可以通过加入 ProductPrice for OrderProduct 在架构外的插入中进行预加载?那么我就不会有N+1 1.?

【问题讨论】:

    标签: elixir phoenix-framework ecto


    【解决方案1】:

    Ecto.Changeset.prepare_changes/2 是另一种选择,可能是您正在寻找的选择:https://hexdocs.pm/ecto/Ecto.Changeset.html#prepare_changes/2

    【讨论】:

    • 感谢 José,这很高兴知道,但 Ecto.Changeset.prepare_changes/2 仅在将更改发送到存储库之前运行。由于我在第一步中也使用Ecto.Changeset.apply_action(changeset, :insert) 而不是Repo.insert/1 来模拟插入,因此不会执行Ecto.Changeset.prepare_changes/2(在最终将购物车插入数据库之前再次向客户显示购物车作为验证步骤)一个额外的步骤)。
    • 也许你可以这样做:如果你要将它插入到数据库中,将更新函数传递给prepare_changes,如果你想之前显示给用户,你直接调用更新函数。这应该不是问题,因为无论如何您都需要调用 apply_action,您可以将两者一起执行并将其封装在一个函数中。
    • 最后我采用了这种方法,非常感谢! ` %Order{} |> change_order(params) |> prepare_changes(&change_order_products(&1, tenant)) |> ... |> Repo.insert(prefix: tenant) `
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-18
    • 2018-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多