【问题标题】:Ecto put_assoc why preload and why require idEcto put_assoc 为什么要预加载以及为什么需要 id
【发布时间】:2017-04-20 20:22:20
【问题描述】:

在向现有模型添加新关联时,我对 Ecto 感到相当困惑。假设我有一些用户可以点赞帖子的讨论板,并且我有以下模型:

  schema "discussion_items" do
    many_to_many :likes, XXX.Tag, join_through: "discussion_items_likes"
    timestamps()
  end

  schema "likes" do
    belongs_to :user, XXX.User
    timestamps()
  end

现在假设我有一个现有的DiscussionItem,我想为此添加一个类似的关联,这就是我正在尝试的(简化):

user = Repo.one(User)
discussion_item = Repo.one(from d in DiscussionItem, preload [:likes])

like_changeset = Like.changeset(%Like{})
                     |> Ecto.Changeset.put_assoc(:user,   user)
changeset = Ecto.Changeset.change(discussion_item) 
                |>  Ecto.Changeset.put_assoc(:likes, [like_changeset])

Ecto.update!(changeset)

我发现这将在第一次正确创建 Like 和关联,但随后抱怨我需要一些 id 形式的其他信息:

您正在尝试更改关系:喜欢 XXX.DiscussionItem, 但缺少数据

如果您尝试更新现有条目,请确保 您在数据旁边包含条目主键 (ID)。

如果你有很多孩子的关系,至少相同的N 必须在更新时给予孩子。默认情况下是不可能的 孤儿嵌入或关联记录,尝试这样做会导致 此错误消息。

以下是让我感到困惑的事情(有一些):

  1. 此错误消息似乎表明,为了更新一个关联项目,我需要提供一个包含所有项目的更改集,包括已更改的项目。这看起来很疯狂,所以我猜我看错了。
  2. 我不想更新点赞关联——我想添加一个。 put_assoc 在这里是不是错误的选择?
  3. 我真的需要预先加载喜欢才能插入一个项目吗?即使在更新时,我也不想加载整个关联集合来更改一个。
  4. 是否需要加载 UserDicussion_item 才能在此处添加点赞?

【问题讨论】:

  • 似乎我找到了答案,put_assoc 需要所有现有项目加上我要添加的项目本身通过更改集运行。这似乎效率很低,所以希望有更好的方法。
  • 您为什么使用many_to_many 来表示喜欢和讨论项目?一次点赞不应该只计算一个讨论项吗?
  • 我有几件我想要喜欢的东西,讨论项目只是其中之一,所以会有几个连接表。反正真的是跑题了,我真正想知道的是如何在不加载世界的情况下在多对多关系中插入一个新项目,上面的例子不是那么重要
  • this 可能是最好的选择。它有点冗长,但我认为一切变得如此简单是值得的。
  • 干杯贾斯汀,很遗憾我必须这样做,但这比替代方案要好得多。

标签: elixir phoenix-framework ecto


【解决方案1】:

此错误消息似乎表明,为了更新一个关联项目,我需要提供一个包含所有项目的更改集,包括已更改的项目。这看起来很疯狂,所以我猜我读错了。

是的,您需要预加载。来自put_assoc 文档:

此函数要求关联已预先加载到变更集结构中。缺少数据将调用关联上定义的 :on_replace 行为。

文档中给出的示例:

# We can associate at any time post and tags together using changesets
post
|> Repo.preload(:tags) # Load existing data
|> Ecto.Changeset.change() # Build the changeset
|> Ecto.Changeset.put_assoc(:tags, [tag]) # Set the association
|> Repo.update!

我不想更新点赞关联——我想添加一个。 put_assoc 在这里是不是错误的选择?

put_assoc 仅在您不介意预加载所有现有的 likes 时才可以。如果你想避免预加载,那么Join Schema 看起来是最好的选择。

我真的需要预先加载喜欢才能插入一个项目吗?即使在更新时,我也不想加载整个关联集合来更改一个。

为了方便使用put_assoc,您需要预加载。 另一种方法是使用Join Schema,允许您独立于任一方的实体显式插入/更新关系。

我需要加载 User 和 Dicussion_item 才能在此处添加赞吗?

不,您只需要 ID。 您可以在参数中填充 user_id 字段来构造 Like 变更集。

同样,使用DiscussionItemLikes 连接架构,您可以设置like_iddiscussion_item_id

Ecto.Multi 可能是在单个事务中创建%Like{}%DiscussionItemLike{} 的一种简洁方式。

Multi.new()
|> Multi.insert(insert_like_changeset(params))
|> Multi.insert(insert_discussion_item_like_changeset(params))
|> Repo.transaction()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-06-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-04
    • 2018-01-07
    • 2019-05-10
    • 1970-01-01
    相关资源
    最近更新 更多